Sidoineko commited on
Commit
2ab7cf1
·
verified ·
1 Parent(s): 3d12d0a

Update src/streamlit_app_stable.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app_stable.py +297 -176
src/streamlit_app_stable.py CHANGED
@@ -5,8 +5,9 @@ import time
5
  import psutil
6
  from transformers import AutoProcessor, Gemma3nForConditionalGeneration
7
  from PIL import Image
 
8
 
9
- # Configuration de la page
10
  st.set_page_config(
11
  page_title="AgriLens AI - Analyse de Plantes",
12
  page_icon="🌱",
@@ -14,7 +15,7 @@ st.set_page_config(
14
  initial_sidebar_state="expanded"
15
  )
16
 
17
- # Initialisation des variables de session
18
  if 'model_loaded' not in st.session_state:
19
  st.session_state.model_loaded = False
20
  if 'model' not in st.session_state:
@@ -26,13 +27,14 @@ if 'model_status' not in st.session_state:
26
  if 'model_load_time' not in st.session_state:
27
  st.session_state.model_load_time = None
28
  if 'language' not in st.session_state:
29
- st.session_state.language = "fr"
30
  if 'load_attempt_count' not in st.session_state:
31
  st.session_state.load_attempt_count = 0
32
  if 'device' not in st.session_state:
33
  st.session_state.device = "cpu"
34
 
35
- # Fonctions d'aide système
 
36
  def check_model_health():
37
  """Vérifie si le modèle et le processeur sont chargés et semblent opérationnels."""
38
  try:
@@ -49,7 +51,7 @@ def diagnose_loading_issues():
49
  try:
50
  ram = psutil.virtual_memory()
51
  ram_gb = ram.total / (1024**3)
52
- if ram_gb < 8:
53
  issues.append(f"⚠️ RAM faible: {ram_gb:.1f}GB (recommandé: 8GB+ pour ce modèle)")
54
  except Exception as e:
55
  issues.append(f"⚠️ Impossible de vérifier la RAM : {e}")
@@ -65,7 +67,7 @@ def diagnose_loading_issues():
65
  if torch.cuda.is_available():
66
  try:
67
  gpu_memory = torch.cuda.get_device_properties(0).total_memory / (1024**3)
68
- if gpu_memory < 6:
69
  issues.append(f"⚠️ GPU mémoire faible: {gpu_memory:.1f}GB (recommandé: 6GB+)")
70
  except Exception as e:
71
  issues.append(f"⚠️ Erreur lors de la vérification de la mémoire GPU : {e}")
@@ -89,11 +91,11 @@ def afficher_ram_disponible(context=""):
89
  ram_used_gb = ram.used / (1024**3)
90
  ram_total_gb = ram.total / (1024**3)
91
  ram_percent = ram.percent
92
- st.write(f"💾 RAM {context}: {ram_used_gb:.1f}GB / {ram_total_gb:.1f}GB ({ram_percent:.1f}%)")
93
  except Exception as e:
94
- st.write(f"💾 Impossible d'afficher l'utilisation de la RAM {context}: {e}")
95
 
96
- # Gestion des traductions
97
  def t(key):
98
  """Fonction pour gérer les traductions."""
99
  translations = {
@@ -110,7 +112,48 @@ def t(key):
110
  "config_title": "⚙️ Configuration & Informations",
111
  "about_title": "ℹ️ À Propos de l'Application",
112
  "load_model": "Charger le Modèle IA",
113
- "model_status": "Statut du Modèle IA"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  },
115
  "en": {
116
  "title": "🌱 AgriLens AI - Plant Analysis Assistant",
@@ -125,76 +168,116 @@ def t(key):
125
  "config_title": "⚙️ Configuration & Information",
126
  "about_title": "ℹ️ About the Application",
127
  "load_model": "Load AI Model",
128
- "model_status": "AI Model Status"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  }
130
  }
131
  return translations[st.session_state.language].get(key, key)
132
 
133
- # Fonctions de chargement et d'analyse du modèle
134
  MODEL_ID_HF = "google/gemma-3n-E4B-it" # ID du modèle sur Hugging Face Hub
135
 
136
  def get_device_map():
137
  """Détermine si le modèle doit être chargé sur GPU ou CPU."""
138
  if torch.cuda.is_available():
139
  st.session_state.device = "cuda"
140
- return "auto"
141
  else:
142
  st.session_state.device = "cpu"
143
  return "cpu"
144
 
145
- def load_model():
 
146
  """
147
  Charge le modèle Gemma 3n et son processeur associé depuis Hugging Face Hub.
148
  Gère les erreurs et les tentatives de chargement.
149
  """
150
- try:
151
- if st.session_state.load_attempt_count >= 3:
152
- st.error("❌ Trop de tentatives de chargement ont échoué. Veuillez vérifier votre configuration et redémarrer l'application.")
153
- return None, None
154
-
155
- st.session_state.load_attempt_count += 1
156
- st.info("🔍 Diagnostic de l'environnement avant chargement...")
157
- issues = diagnose_loading_issues()
158
- if issues:
159
- with st.expander("📊 Diagnostic système", expanded=False):
160
- for issue in issues:
161
- st.write(issue)
162
-
163
- gc.collect()
164
- if torch.cuda.is_available():
165
- torch.cuda.empty_cache()
166
- device_map = get_device_map()
167
 
168
- try:
169
- st.info(f"Chargement du modèle depuis Hugging Face Hub : {MODEL_ID_HF}...")
170
- processor = AutoProcessor.from_pretrained(MODEL_ID_HF, trust_remote_code=True)
171
- model = Gemma3nForConditionalGeneration.from_pretrained(
172
- MODEL_ID_HF,
173
- torch_dtype=torch.bfloat16 if device_map == "auto" else torch.float32,
174
- trust_remote_code=True,
175
- low_cpu_mem_usage=True,
176
- device_map=device_map
177
- )
178
- st.success(f"✅ Modèle chargé avec succès depuis Hugging Face Hub ({MODEL_ID_HF}).")
179
- st.session_state.model_status = "Chargé (Hub)"
180
 
181
- st.session_state.model = model
182
- st.session_state.processor = processor
183
- st.session_state.model_loaded = True
184
- st.session_state.model_load_time = time.time()
185
- st.session_state.load_attempt_count = 0
186
 
187
- return model, processor
188
 
189
- except Exception as e:
190
- st.error(f" Échec du chargement du modèle depuis Hugging Face Hub : {e}")
191
- return None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
- except ImportError:
194
- st.error("❌ Erreur : Les bibliothèques `transformers` ou `torch` ne sont pas installées.")
195
  return None, None
196
  except Exception as e:
197
- st.error(f" Erreur générale lors du chargement du modèle : {e}")
198
  return None, None
199
 
200
  def analyze_image_multilingual(image, prompt_text=""):
@@ -203,15 +286,16 @@ def analyze_image_multilingual(image, prompt_text=""):
203
  Retourne le résultat de l'analyse.
204
  """
205
  if not st.session_state.model_loaded or not check_model_health():
206
- st.error("❌ Modèle IA non chargé ou non fonctionnel. Veuillez le charger via la barre latérale.")
207
  return None
208
 
209
  try:
210
  if image.mode != 'RGB':
211
  image = image.convert('RGB')
212
 
213
- if not prompt_text:
214
- prompt_text = """<image>
 
215
  Analyse cette image de plante et fournis un diagnostic complet :
216
  1. **État général de la plante :** Décris son apparence globale et sa vitalité.
217
  2. **Identification des problèmes :** Liste les maladies, parasites ou carences visibles.
@@ -220,7 +304,18 @@ Analyse cette image de plante et fournis un diagnostic complet :
220
  5. **Recommandations de traitement :** Propose des solutions concrètes et adaptées.
221
  6. **Conseils préventifs :** Donne des astuces pour éviter que le problème ne revienne.
222
  Réponds de manière structurée et claire en français."""
223
-
 
 
 
 
 
 
 
 
 
 
 
224
  if "<image>" not in prompt_text:
225
  prompt_text = "<image>\n" + prompt_text
226
 
@@ -230,10 +325,12 @@ Réponds de manière structurée et claire en français."""
230
  return_tensors="pt"
231
  )
232
 
 
233
  inputs = {key: val.to(st.session_state.model.device) for key, val in inputs.items()}
234
 
235
- with st.spinner("🔍 Analyse d'image en cours..."):
236
  input_len = inputs["input_ids"].shape[-1]
 
237
  outputs = st.session_state.model.generate(
238
  **inputs,
239
  max_new_tokens=512,
@@ -241,13 +338,13 @@ Réponds de manière structurée et claire en français."""
241
  temperature=0.7,
242
  top_p=0.9
243
  )
244
- generation = outputs[0][input_len:]
245
  response = st.session_state.processor.decode(generation, skip_special_tokens=True)
246
 
247
  return response.strip()
248
 
249
  except Exception as e:
250
- st.error(f"❌ Erreur lors de l'analyse de l'image : {e}")
251
  return None
252
 
253
  def analyze_text_multilingual(text_description):
@@ -256,28 +353,44 @@ def analyze_text_multilingual(text_description):
256
  Retourne le diagnostic et les recommandations.
257
  """
258
  if not st.session_state.model_loaded or not check_model_health():
259
- st.error("❌ Modèle IA non chargé ou non fonctionnel. Veuillez le charger via la barre latérale.")
260
  return None
261
 
262
  try:
263
- prompt = f"""Analyse la description des symptômes de cette plante et fournis un diagnostic détaillé :
 
 
264
  **Description des symptômes :**
265
- {text_description}
266
  **Instructions :**
267
  1. **Diagnostic probable :** Quel est le problème principal ?
268
  2. **Causes possibles :** Pourquoi ce problème survient-il ?
269
  3. **Traitement recommandé :** Comment le résoudre ?
270
  4. **Conseils préventifs :** Comment l'éviter à l'avenir ?
271
  Réponds en français de manière claire et structurée."""
 
 
 
 
 
 
 
 
 
 
 
 
 
272
 
273
  inputs = st.session_state.processor(
274
- text=prompt,
275
  return_tensors="pt"
276
  )
277
 
 
278
  inputs = {key: val.to(st.session_state.model.device) for key, val in inputs.items()}
279
 
280
- with st.spinner("🔍 Analyse textuelle en cours..."):
281
  outputs = st.session_state.model.generate(
282
  **inputs,
283
  max_new_tokens=512,
@@ -285,28 +398,29 @@ Réponds en français de manière claire et structurée."""
285
  temperature=0.7,
286
  top_p=0.9
287
  )
 
288
 
289
- response = st.session_state.processor.decode(outputs[0], skip_special_tokens=True)
290
-
291
- if prompt.strip() in response:
292
- response_only = response.split(prompt.strip())[-1].strip()
293
  else:
294
  response_only = response.strip()
295
 
296
  return response_only
297
 
298
  except Exception as e:
299
- st.error(f"❌ Erreur lors de l'analyse textuelle : {e}")
300
  return None
301
 
302
- # Interface Utilisateur Streamlit
303
  st.title(t("title"))
304
  st.markdown(t("subtitle"))
305
 
306
- # Barre Latérale (Sidebar) pour la Configuration
307
  with st.sidebar:
308
  st.header(t("config_title"))
309
 
 
310
  lang_selector_options = ["Français", "English"]
311
  current_lang_index = 0 if st.session_state.language == "fr" else 1
312
  language_selected = st.selectbox(
@@ -319,47 +433,54 @@ with st.sidebar:
319
 
320
  st.divider()
321
 
 
322
  st.header(t("model_status"))
323
-
324
  if st.session_state.model_loaded and check_model_health():
325
- st.success("✅ Modèle chargé et fonctionnel")
326
- st.write(f"**Statut :** `{st.session_state.model_status}`")
327
  if st.session_state.model_load_time:
328
  load_time_str = time.strftime('%H:%M:%S', time.localtime(st.session_state.model_load_time))
329
- st.write(f"**Heure de chargement :** {load_time_str}")
330
 
331
- if st.button("🔄 Recharger le modèle", type="secondary"):
332
  st.session_state.model_loaded = False
333
  st.session_state.model = None
334
  st.session_state.processor = None
335
  st.session_state.model_status = "Non chargé"
336
  st.session_state.load_attempt_count = 0
337
- st.info("Modèle déchargé. Cliquez sur 'Charger le modèle IA' pour le recharger.")
 
338
  else:
339
- st.warning("⚠️ Modèle IA non chargé")
340
 
341
  if st.button(t("load_model"), type="primary"):
342
- with st.spinner("🔄 Chargement du modèle IA en cours..."):
343
- model_loaded_success = load_model()
344
- if model_loaded_success[0] is not None and model_loaded_success[1] is not None:
345
- st.success("✅ Modèle IA chargé avec succès !")
 
 
 
346
  else:
347
- st.error("❌ Échec du chargement du modèle IA.")
 
348
 
349
  st.divider()
350
- st.subheader("📊 Ressources Système")
 
351
  afficher_ram_disponible()
352
  if torch.cuda.is_available():
353
  try:
354
  gpu_memory = torch.cuda.memory_allocated(0) / (1024**3)
355
  gpu_total = torch.cuda.get_device_properties(0).total_memory / (1024**3)
356
- st.write(f"🚀 GPU actuel : {gpu_memory:.1f}GB / {gpu_total:.1f}GB")
 
357
  except Exception as e:
358
- st.write(f"🚀 Erreur lors de la récupération des informations GPU : {e}")
359
  else:
360
- st.write("🚀 GPU : Non disponible (utilisation CPU)")
361
 
362
- # Onglets Principaux
363
  tab1, tab2, tab3, tab4 = st.tabs(t("tabs"))
364
 
365
  with tab1:
@@ -367,9 +488,8 @@ with tab1:
367
  st.markdown(t("image_analysis_desc"))
368
 
369
  capture_option = st.radio(
370
- "Choisissez votre méthode de capture :",
371
- ["📁 Upload d'image" if st.session_state.language == "fr" else "📁 Upload Image",
372
- "📷 Capture par webcam" if st.session_state.language == "fr" else "📷 Webcam Capture"],
373
  horizontal=True,
374
  key="image_capture_method"
375
  )
@@ -377,98 +497,102 @@ with tab1:
377
  uploaded_file = None
378
  captured_image = None
379
 
380
- if capture_option == ("📁 Upload d'image" if st.session_state.language == "fr" else "📁 Upload Image"):
381
  uploaded_file = st.file_uploader(
382
  t("choose_image"),
383
  type=['png', 'jpg', 'jpeg'],
384
- help="Formats acceptés : PNG, JPG, JPEG (taille max recommandée : 10MB)."
 
385
  )
386
  if uploaded_file is not None and uploaded_file.size > 10 * 1024 * 1024:
387
- st.warning("Le fichier est très volumineux. Il est recommandé d'utiliser des images de taille raisonnable pour une analyse plus rapide.")
388
- else:
389
- st.markdown("**📷 Capture d'image par webcam**")
390
- st.info("Positionnez votre plante malade devant la webcam et cliquez sur 'Prendre une photo'.")
391
- captured_image = st.camera_input("Prendre une photo de la plante")
392
 
393
  image_to_analyze = None
394
  if uploaded_file is not None:
395
  try:
396
  image_to_analyze = Image.open(uploaded_file)
397
  except Exception as e:
398
- st.error(f" Erreur lors du traitement du fichier uploadé : {e}")
399
  elif captured_image is not None:
400
  try:
401
  image_to_analyze = Image.open(captured_image)
402
  except Exception as e:
403
- st.error(f"❌ Erreur lors du traitement de l'image capturée : {e}")
404
 
405
  if image_to_analyze is not None:
406
- original_size = image_to_analyze.size
407
  resized_image, was_resized = resize_image_if_needed(image_to_analyze)
408
 
409
  col1, col2 = st.columns([1, 1])
410
  with col1:
411
- st.image(resized_image, caption="Image à analyser", use_container_width=True)
412
  if was_resized:
413
- st.info(f"ℹ️ Image redimensionnée de {original_size} à {resized_image.size} pour l'analyse.")
414
 
415
  with col2:
416
  if st.session_state.model_loaded and check_model_health():
417
- st.subheader("Options d'analyse")
 
 
 
 
 
 
 
418
  analysis_type = st.selectbox(
419
- "Type d'analyse souhaitée :",
420
- [
421
- "Diagnostic complet (maladie, soins, prévention)" if st.session_state.language == "fr" else "Full Diagnosis (disease, care, prevention)",
422
- "Identification et diagnostic de maladies" if st.session_state.language == "fr" else "Disease Identification and Diagnosis",
423
- "Conseils de soins et d'entretien" if st.session_state.language == "fr" else "Care and Maintenance Advice"
424
- ],
425
  key="image_analysis_type_selector"
426
  )
427
 
428
  custom_prompt_input = st.text_area(
429
- "Prompt personnalisé (optionnel) :",
430
  value="",
431
  height=100,
432
- placeholder="Entrez une requête spécifique ici si besoin..."
433
  )
434
 
435
- if st.button("🔍 Analyser l'image", type="primary", key="analyze_image_button"):
436
  final_prompt = custom_prompt_input.strip()
 
437
  if not final_prompt:
438
- if analysis_type.startswith("Diagnostic complet"):
439
- final_prompt = """Analyse cette image de plante et fournis un diagnostic complet :
440
- 1. **État général de la plante :** Décris son apparence globale et sa vitalité.
441
- 2. **Identification des problèmes :** Liste les maladies, parasites ou carences visibles.
442
- 3. **Diagnostic probable :** Indique la maladie ou le problème le plus probable.
443
- 4. **Causes possibles :** Explique ce qui a pu causer ce problème.
444
- 5. **Recommandations de traitement :** Propose des solutions concrètes et adaptées.
445
- 6. **Conseils préventifs :** Donne des astuces pour éviter que le problème ne revienne.
446
- Réponds de manière structurée et claire en français."""
447
- elif analysis_type.startswith("Identification et diagnostic de maladies"):
448
- final_prompt = """Diagnostique cette plante en te concentrant sur les maladies et parasites :
449
- 1. Identifie les symptômes visuels spécifiques aux maladies ou parasites.
450
- 2. Détermine la maladie ou le parasite le plus probable.
451
- 3. Explique les conditions favorisant leur développement.
452
- 4. Propose des traitements ciblés et des méthodes de lutte.
453
- Réponds en français de manière structurée."""
454
- else:
455
- final_prompt = """Analyse cette plante et donne des conseils de soins détaillés :
456
- 1. État général de la plante : Évalue sa santé actuelle.
457
- 2. Besoins spécifiques : Précise ses besoins en eau, lumière, nutriments et substrat.
458
- 3. Conseils d'entretien : Donne des instructions pour l'arrosage, la fertilisation et la taille.
459
- 4. Améliorations recommandées : Suggère des actions pour optimiser sa croissance et sa santé.
460
- Réponds en français de manière structurée."""
 
461
 
462
  analysis_result = analyze_image_multilingual(resized_image, prompt_text=final_prompt)
463
 
464
  if analysis_result:
465
- st.success("✅ Analyse terminée !")
466
- st.markdown("### 📋 Résultats de l'analyse")
467
  st.markdown(analysis_result)
468
  else:
469
- st.error("❌ Échec de l'analyse de l'image.")
470
  else:
471
- st.warning("⚠️ Modèle IA non chargé. Veuillez d'abord charger le modèle depuis la barre latérale.")
472
 
473
  with tab2:
474
  st.header(t("text_analysis_title"))
@@ -477,24 +601,24 @@ with tab2:
477
  text_description_input = st.text_area(
478
  t("enter_description"),
479
  height=200,
480
- placeholder="Décrivez ici les symptômes observés sur votre plante : feuilles jaunes, taches, flétrissement, présence d'insectes, etc."
481
  )
482
 
483
- if st.button("🔍 Analyser la description", type="primary", key="analyze_text_button"):
484
  if text_description_input.strip():
485
  if st.session_state.model_loaded and check_model_health():
486
  analysis_result = analyze_text_multilingual(text_description_input)
487
 
488
  if analysis_result:
489
- st.success("✅ Analyse terminée !")
490
- st.markdown("### 📋 Résultats de l'analyse")
491
  st.markdown(analysis_result)
492
  else:
493
- st.error("❌ Échec de l'analyse textuelle.")
494
  else:
495
- st.warning("⚠️ Modèle IA non chargé. Veuillez d'abord charger le modèle depuis la barre latérale.")
496
  else:
497
- st.warning("⚠️ Veuillez entrer une description des symptômes de votre plante.")
498
 
499
  with tab3:
500
  st.header(t("config_title"))
@@ -502,45 +626,42 @@ with tab3:
502
  col1, col2 = st.columns(2)
503
 
504
  with col1:
505
- st.subheader("🔧 Informations Système")
506
-
507
  try:
508
  ram = psutil.virtual_memory()
509
- st.write(f"**RAM Totale :** {ram.total / (1024**3):.1f} GB")
510
- st.write(f"**RAM Utilisée :** {ram.used / (1024**3):.1f} GB ({ram.percent:.1f}%)")
511
 
512
  disk = psutil.disk_usage('/')
513
- st.write(f"**Espace Disque Libre (/) :** {disk.free / (1024**3):.1f} GB")
514
 
515
  if torch.cuda.is_available():
516
- st.write(f"**GPU Détecté :** {torch.cuda.get_device_name(0)}")
517
  gpu_props = torch.cuda.get_device_properties(0)
518
- st.write(f"**Mémoire GPU Totale :** {gpu_props.total_memory / (1024**3):.1f} GB")
519
  else:
520
- st.write("**GPU :** Non disponible (fonctionnement sur CPU)")
521
  except Exception as e:
522
- st.error(f"Erreur lors de la récupération des informations système : {e}")
523
 
524
  with col2:
525
- st.subheader("📊 Statistiques du Modèle IA")
526
-
527
  if st.session_state.model_loaded and check_model_health():
528
- st.write("**Statut :** ✅ Chargé et fonctionnel")
529
  if st.session_state.model is not None:
530
- st.write(f"**Type de modèle :** `{type(st.session_state.model).__name__}`")
531
  if hasattr(st.session_state.model, 'device'):
532
- st.write(f"**Device utilisé :** `{st.session_state.model.device}`")
533
  if st.session_state.model_load_time:
534
  load_time_str = time.strftime('%H:%M:%S', time.localtime(st.session_state.model_load_time))
535
- st.write(f"**Heure de chargement :** {load_time_str}")
536
  else:
537
- st.write("**Statut :** ❌ Non chargé")
538
- st.write("**Type de modèle :** N/A")
539
- st.write("**Device utilisé :** N/A")
540
 
541
  with tab4:
542
  st.header(t("about_title"))
543
-
544
  st.markdown("""
545
  ## 🌱 AgriLens AI : Votre Assistant d'Analyse de Plantes
546
  **AgriLens AI** est une application alimentée par l'intelligence artificielle qui vous aide à identifier les problèmes de vos plantes, à diagnostiquer les maladies et à obtenir des conseils de traitement personnalisés. Que vous soyez un jardinier débutant ou expérimenté, AgriLens AI est là pour vous accompagner.
@@ -550,10 +671,10 @@ with tab4:
550
  - **Recommandations Précises :** Recevez des conseils de traitement, des suggestions de soins et des mesures préventives adaptées à chaque situation.
551
  - **Support Multilingue :** L'interface et les réponses sont disponibles en français et en anglais pour une accessibilité maximale.
552
  ### 🤖 Technologie Utilisée :
553
- - **Modèle IA :** Google Gemma 3n E4B IT, un modèle multimodal performant pour l'analyse de plantes.
554
  - **Bibliothèques :** `transformers`, `torch`, `streamlit`, `Pillow`, `psutil`, `requests`, `huggingface-hub`.
555
  ### 📝 Comment Utiliser AgriLens AI :
556
- 1. **Chargez le Modèle IA :** Dans la barre latérale, cliquez sur "Charger le Modèle IA". Attendez que le statut passe à "Chargé et fonctionnel".
557
  2. **Analysez votre Plante :**
558
  - **Via Image :** Allez à l'onglet "📸 Analyse d'Image". Uploadez une photo de votre plante ou utilisez votre webcam. Choisissez le type d'analyse et cliquez sur "Analyser l'image".
559
  - **Via Texte :** Allez à l'onglet "📝 Analyse de Texte". Décrivez les symptômes de votre plante dans la zone de texte et cliquez sur "Analyser la description".
@@ -565,13 +686,13 @@ with tab4:
565
  *Développé avec passion pour aider les jardiniers et amoureux des plantes à prendre soin de leurs cultures grâce à la puissance de l'IA.*
566
  """)
567
 
568
- # Pied de page
569
  st.divider()
570
  st.markdown("""
571
  <div style='text-align: center; color: #666;'>
572
- 🌱 AgriLens AI - Assistant d'Analyse de Plantes |
573
- <a href='#' target='_blank'>Documentation</a> |
574
- <a href='#' target='_blank'>Support</a> |
575
- <a href='https://huggingface.co/google/gemma-3n-E4B-it' target='_blank'>Modèle Gemma 3n</a>
576
  </div>
577
  """, unsafe_allow_html=True)
 
5
  import psutil
6
  from transformers import AutoProcessor, Gemma3nForConditionalGeneration
7
  from PIL import Image
8
+ import os # Importé pour l'exemple d'utilisation de `max_upload_size`
9
 
10
+ # --- Configuration de la Page ---
11
  st.set_page_config(
12
  page_title="AgriLens AI - Analyse de Plantes",
13
  page_icon="🌱",
 
15
  initial_sidebar_state="expanded"
16
  )
17
 
18
+ # --- Initialisation des Variables de Session ---
19
  if 'model_loaded' not in st.session_state:
20
  st.session_state.model_loaded = False
21
  if 'model' not in st.session_state:
 
27
  if 'model_load_time' not in st.session_state:
28
  st.session_state.model_load_time = None
29
  if 'language' not in st.session_state:
30
+ st.session_state.language = "fr" # Langue par défaut
31
  if 'load_attempt_count' not in st.session_state:
32
  st.session_state.load_attempt_count = 0
33
  if 'device' not in st.session_state:
34
  st.session_state.device = "cpu"
35
 
36
+ # --- Fonctions d'Aide Système ---
37
+
38
  def check_model_health():
39
  """Vérifie si le modèle et le processeur sont chargés et semblent opérationnels."""
40
  try:
 
51
  try:
52
  ram = psutil.virtual_memory()
53
  ram_gb = ram.total / (1024**3)
54
+ if ram_gb < 8: # Recommandation générale, le modèle peut nécessiter plus
55
  issues.append(f"⚠️ RAM faible: {ram_gb:.1f}GB (recommandé: 8GB+ pour ce modèle)")
56
  except Exception as e:
57
  issues.append(f"⚠️ Impossible de vérifier la RAM : {e}")
 
67
  if torch.cuda.is_available():
68
  try:
69
  gpu_memory = torch.cuda.get_device_properties(0).total_memory / (1024**3)
70
+ if gpu_memory < 6: # Recommandation générale, le modèle peut nécessiter plus
71
  issues.append(f"⚠️ GPU mémoire faible: {gpu_memory:.1f}GB (recommandé: 6GB+)")
72
  except Exception as e:
73
  issues.append(f"⚠️ Erreur lors de la vérification de la mémoire GPU : {e}")
 
91
  ram_used_gb = ram.used / (1024**3)
92
  ram_total_gb = ram.total / (1024**3)
93
  ram_percent = ram.percent
94
+ st.write(f"💾 RAM : {ram_used_gb:.1f}GB / {ram_total_gb:.1f}GB ({ram_percent:.1f}%)")
95
  except Exception as e:
96
+ st.write(f"💾 Impossible d'afficher l'utilisation de la RAM : {e}")
97
 
98
+ # --- Gestion des Traductions ---
99
  def t(key):
100
  """Fonction pour gérer les traductions."""
101
  translations = {
 
112
  "config_title": "⚙️ Configuration & Informations",
113
  "about_title": "ℹ️ À Propos de l'Application",
114
  "load_model": "Charger le Modèle IA",
115
+ "model_status": "Statut du Modèle IA",
116
+ "upload_image_label": "📁 Upload d'image",
117
+ "webcam_capture_label": "📷 Capture par webcam",
118
+ "image_analysis_options_title": "Options d'analyse",
119
+ "analysis_type_full_diagnosis": "Diagnostic complet (maladie, soins, prévention)",
120
+ "analysis_type_disease_id": "Identification et diagnostic de maladies",
121
+ "analysis_type_care_advice": "Conseils de soins et d'entretien",
122
+ "custom_prompt_label": "Prompt personnalisé (optionnel) :",
123
+ "analyze_image_button": "🔍 Analyser l'image",
124
+ "analyze_text_button": "🔍 Analyser la description",
125
+ "model_not_loaded_warning": "⚠️ Modèle IA non chargé. Veuillez d'abord charger le modèle depuis la barre latérale.",
126
+ "system_info_title": "🔧 Informations Système",
127
+ "model_stats_title": "📊 Statistiques du Modèle IA",
128
+ "model_status_loaded": "✅ Modèle chargé et fonctionnel",
129
+ "model_status_not_loaded": "❌ Modèle IA non chargé",
130
+ "model_type_label": "Type de modèle :",
131
+ "device_used_label": "Device utilisé :",
132
+ "load_time_label": "Heure de chargement :",
133
+ "reload_model_button": "🔄 Recharger le modèle",
134
+ "model_unloaded_info": "Modèle déchargé. Cliquez sur 'Charger le modèle IA' pour le recharger.",
135
+ "loading_model_spinner": "🔄 Chargement du modèle IA en cours...",
136
+ "analysis_in_progress_image": "🔍 Analyse d'image en cours...",
137
+ "analysis_in_progress_text": "🔍 Analyse textuelle en cours...",
138
+ "analysis_complete_success": "✅ Analyse terminée !",
139
+ "analysis_failed_error": "❌ Échec de l'analyse de l'image.",
140
+ "text_analysis_failed_error": "❌ Échec de l'analyse textuelle.",
141
+ "results_title": "📋 Résultats de l'analyse",
142
+ "sys_ram_total": "RAM Totale :",
143
+ "sys_ram_used": "RAM Utilisée :",
144
+ "sys_disk_free": "Espace Disque Libre (/) :",
145
+ "sys_gpu_detected": "GPU Détecté :",
146
+ "sys_gpu_total_mem": "Mémoire GPU Totale :",
147
+ "sys_gpu_not_available": "GPU : Non disponible (fonctionnement sur CPU)",
148
+ "sys_info_error": "Erreur lors de la récupération des informations système :",
149
+ "model_loading_error": "❌ Trop de tentatives de chargement ont échoué. Veuillez vérifier votre configuration et redémarrer l'application.",
150
+ "model_loading_hub_error": "❌ Échec du chargement du modèle depuis Hugging Face Hub : ",
151
+ "image_processing_error": "❌ Erreur lors du traitement du fichier uploadé : ",
152
+ "image_capture_processing_error": "❌ Erreur lors du traitement de l'image capturée : ",
153
+ "image_resized_info": "ℹ️ Image redimensionnée de {} à {} pour l'analyse.",
154
+ "choose_capture_method_label": "Choisissez votre méthode de capture :",
155
+ "webcam_instructions": "Positionnez votre plante malade devant la webcam et cliquez sur 'Prendre une photo'.",
156
+ "file_size_warning": "Le fichier est très volumineux. Il est recommandé d'utiliser des images de taille raisonnable pour une analyse plus rapide."
157
  },
158
  "en": {
159
  "title": "🌱 AgriLens AI - Plant Analysis Assistant",
 
168
  "config_title": "⚙️ Configuration & Information",
169
  "about_title": "ℹ️ About the Application",
170
  "load_model": "Load AI Model",
171
+ "model_status": "AI Model Status",
172
+ "upload_image_label": "📁 Upload Image",
173
+ "webcam_capture_label": "📷 Webcam Capture",
174
+ "image_analysis_options_title": "Analysis Options",
175
+ "analysis_type_full_diagnosis": "Full Diagnosis (disease, care, prevention)",
176
+ "analysis_type_disease_id": "Disease Identification and Diagnosis",
177
+ "analysis_type_care_advice": "Care and Maintenance Advice",
178
+ "custom_prompt_label": "Custom prompt (optional) :",
179
+ "analyze_image_button": "🔍 Analyze Image",
180
+ "analyze_text_button": "🔍 Analyze Description",
181
+ "model_not_loaded_warning": "⚠️ AI Model not loaded. Please load the model from the sidebar first.",
182
+ "system_info_title": "🔧 System Information",
183
+ "model_stats_title": "📊 AI Model Statistics",
184
+ "model_status_loaded": "✅ Model Loaded and Functional",
185
+ "model_status_not_loaded": "❌ AI Model Not Loaded",
186
+ "model_type_label": "Model Type:",
187
+ "device_used_label": "Device Used:",
188
+ "load_time_label": "Load Time:",
189
+ "reload_model_button": "🔄 Reload Model",
190
+ "model_unloaded_info": "Model unloaded. Click 'Load AI Model' to reload it.",
191
+ "loading_model_spinner": "🔄 Loading AI Model...",
192
+ "analysis_in_progress_image": "🔍 Analyzing image...",
193
+ "analysis_in_progress_text": "🔍 Analyzing text...",
194
+ "analysis_complete_success": "✅ Analysis complete!",
195
+ "analysis_failed_error": "❌ Image analysis failed.",
196
+ "text_analysis_failed_error": "❌ Text analysis failed.",
197
+ "results_title": "📋 Analysis Results",
198
+ "sys_ram_total": "Total RAM:",
199
+ "sys_ram_used": "RAM Used:",
200
+ "sys_disk_free": "Free Disk Space (/):",
201
+ "sys_gpu_detected": "GPU Detected:",
202
+ "sys_gpu_total_mem": "Total GPU Memory:",
203
+ "sys_gpu_not_available": "GPU: Not Available (CPU operation)",
204
+ "sys_info_error": "Error retrieving system information:",
205
+ "model_loading_error": "❌ Too many failed loading attempts. Please check your configuration and restart the application.",
206
+ "model_loading_hub_error": "❌ Failed to load model from Hugging Face Hub: ",
207
+ "image_processing_error": "❌ Error processing uploaded file: ",
208
+ "image_capture_processing_error": "❌ Error processing captured image: ",
209
+ "image_resized_info": "ℹ️ Image resized from {} to {} for analysis.",
210
+ "choose_capture_method_label": "Choose your capture method:",
211
+ "webcam_instructions": "Position your sick plant in front of the webcam and click 'Take Photo'.",
212
+ "file_size_warning": "The file is very large. It's recommended to use reasonably sized images for faster analysis."
213
  }
214
  }
215
  return translations[st.session_state.language].get(key, key)
216
 
217
+ # --- Fonctions de chargement et d'analyse du modèle ---
218
  MODEL_ID_HF = "google/gemma-3n-E4B-it" # ID du modèle sur Hugging Face Hub
219
 
220
  def get_device_map():
221
  """Détermine si le modèle doit être chargé sur GPU ou CPU."""
222
  if torch.cuda.is_available():
223
  st.session_state.device = "cuda"
224
+ return "auto" # Laisse transformers décider de la meilleure répartition
225
  else:
226
  st.session_state.device = "cpu"
227
  return "cpu"
228
 
229
+ @st.cache_resource # Utilisation du cache Streamlit pour le modèle
230
+ def load_model_and_processor():
231
  """
232
  Charge le modèle Gemma 3n et son processeur associé depuis Hugging Face Hub.
233
  Gère les erreurs et les tentatives de chargement.
234
  """
235
+ if st.session_state.load_attempt_count >= 3:
236
+ st.error(t("model_loading_error"))
237
+ return None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
+ st.session_state.load_attempt_count += 1
240
+ st.info("🔍 Diagnostic de l'environnement avant chargement...")
241
+ issues = diagnose_loading_issues()
242
+ if issues:
243
+ with st.expander("📊 Diagnostic système", expanded=False):
244
+ for issue in issues:
245
+ st.write(issue)
 
 
 
 
 
246
 
247
+ gc.collect() # Nettoyage de la mémoire
248
+ if torch.cuda.is_available():
249
+ torch.cuda.empty_cache() # Vide le cache GPU
 
 
250
 
251
+ device_map = get_device_map()
252
 
253
+ try:
254
+ st.info(f"Chargement du modèle depuis Hugging Face Hub : `{MODEL_ID_HF}`...")
255
+
256
+ # Configuration du torch_dtype pour une meilleure efficacité sur GPU
257
+ torch_dtype = torch.bfloat16 if st.session_state.device == "cuda" else torch.float32
258
+
259
+ processor = AutoProcessor.from_pretrained(MODEL_ID_HF, trust_remote_code=True)
260
+ model = Gemma3nForConditionalGeneration.from_pretrained(
261
+ MODEL_ID_HF,
262
+ torch_dtype=torch_dtype,
263
+ trust_remote_code=True,
264
+ low_cpu_mem_usage=True, # Tente de réduire l'utilisation CPU pendant le chargement
265
+ device_map=device_map
266
+ )
267
+
268
+ st.success(f"✅ Modèle `{MODEL_ID_HF}` chargé avec succès depuis Hugging Face Hub.")
269
+ st.session_state.model_status = "Chargé (Hub)"
270
+ st.session_state.model_loaded = True
271
+ st.session_state.model_load_time = time.time()
272
+ st.session_state.load_attempt_count = 0 # Réinitialise le compteur si succès
273
+
274
+ return model, processor
275
 
276
+ except ImportError as e:
277
+ st.error(f"❌ Erreur : Les bibliothèques `transformers` ou `torch` ne sont pas installées. Détails: {e}")
278
  return None, None
279
  except Exception as e:
280
+ st.error(f"{t('model_loading_hub_error')} {e}")
281
  return None, None
282
 
283
  def analyze_image_multilingual(image, prompt_text=""):
 
286
  Retourne le résultat de l'analyse.
287
  """
288
  if not st.session_state.model_loaded or not check_model_health():
289
+ st.error(t("model_not_loaded_warning"))
290
  return None
291
 
292
  try:
293
  if image.mode != 'RGB':
294
  image = image.convert('RGB')
295
 
296
+ if not prompt_text: # Utilisation des prompts par défaut basés sur la langue
297
+ if st.session_state.language == "fr":
298
+ prompt_text = """<image>
299
  Analyse cette image de plante et fournis un diagnostic complet :
300
  1. **État général de la plante :** Décris son apparence globale et sa vitalité.
301
  2. **Identification des problèmes :** Liste les maladies, parasites ou carences visibles.
 
304
  5. **Recommandations de traitement :** Propose des solutions concrètes et adaptées.
305
  6. **Conseils préventifs :** Donne des astuces pour éviter que le problème ne revienne.
306
  Réponds de manière structurée et claire en français."""
307
+ else: # English
308
+ prompt_text = """<image>
309
+ Analyze this plant image and provide a comprehensive diagnosis:
310
+ 1. **Overall Plant Health:** Describe its general appearance and vitality.
311
+ 2. **Problem Identification:** List visible diseases, pests, or deficiencies.
312
+ 3. **Probable Diagnosis:** Indicate the most likely disease or problem.
313
+ 4. **Possible Causes:** Explain what might have caused this issue.
314
+ 5. **Treatment Recommendations:** Propose concrete and appropriate solutions.
315
+ 6. **Preventive Advice:** Give tips to avoid the problem from recurring.
316
+ Respond in a structured and clear manner in English."""
317
+
318
+ # Assure que le prompt commence par <image> si ce n'est pas déjà le cas
319
  if "<image>" not in prompt_text:
320
  prompt_text = "<image>\n" + prompt_text
321
 
 
325
  return_tensors="pt"
326
  )
327
 
328
+ # Déplace les tenseurs sur le bon device
329
  inputs = {key: val.to(st.session_state.model.device) for key, val in inputs.items()}
330
 
331
+ with st.spinner(t("analysis_in_progress_image")):
332
  input_len = inputs["input_ids"].shape[-1]
333
+ # Paramètres de génération ajustables si nécessaire
334
  outputs = st.session_state.model.generate(
335
  **inputs,
336
  max_new_tokens=512,
 
338
  temperature=0.7,
339
  top_p=0.9
340
  )
341
+ generation = outputs[0][input_len:] # Garde uniquement les tokens générés
342
  response = st.session_state.processor.decode(generation, skip_special_tokens=True)
343
 
344
  return response.strip()
345
 
346
  except Exception as e:
347
+ st.error(f"{t('analysis_failed_error')} {e}")
348
  return None
349
 
350
  def analyze_text_multilingual(text_description):
 
353
  Retourne le diagnostic et les recommandations.
354
  """
355
  if not st.session_state.model_loaded or not check_model_health():
356
+ st.error(t("model_not_loaded_warning"))
357
  return None
358
 
359
  try:
360
+ # Définition du prompt en fonction de la langue sélectionnée
361
+ if st.session_state.language == "fr":
362
+ prompt = f"""Analyse la description des symptômes de cette plante et fournis un diagnostic détaillé :
363
  **Description des symptômes :**
364
+
365
  **Instructions :**
366
  1. **Diagnostic probable :** Quel est le problème principal ?
367
  2. **Causes possibles :** Pourquoi ce problème survient-il ?
368
  3. **Traitement recommandé :** Comment le résoudre ?
369
  4. **Conseils préventifs :** Comment l'éviter à l'avenir ?
370
  Réponds en français de manière claire et structurée."""
371
+ else: # English
372
+ prompt = f"""Analyze the description of this plant's symptoms and provide a detailed diagnosis:
373
+ **Symptoms Description:**
374
+
375
+ **Instructions:**
376
+ 1. **Probable Diagnosis:** What is the main issue?
377
+ 2. **Possible Causes:** Why is this problem occurring?
378
+ 3. **Recommended Treatment:** How to resolve it?
379
+ 4. **Preventive Advice:** How to avoid it in the future?
380
+ Respond in English in a clear and structured manner."""
381
+
382
+ # Ajoute la description de l'utilisateur au prompt
383
+ full_prompt = prompt + "\n\n" + text_description
384
 
385
  inputs = st.session_state.processor(
386
+ text=full_prompt,
387
  return_tensors="pt"
388
  )
389
 
390
+ # Déplace les tenseurs sur le bon device
391
  inputs = {key: val.to(st.session_state.model.device) for key, val in inputs.items()}
392
 
393
+ with st.spinner(t("analysis_in_progress_text")):
394
  outputs = st.session_state.model.generate(
395
  **inputs,
396
  max_new_tokens=512,
 
398
  temperature=0.7,
399
  top_p=0.9
400
  )
401
+ response = st.session_state.processor.decode(outputs[0], skip_special_tokens=True)
402
 
403
+ # Nettoie le prompt du début de la réponse si le modèle l'inclut
404
+ if full_prompt.strip() in response:
405
+ response_only = response.split(full_prompt.strip())[-1].strip()
 
406
  else:
407
  response_only = response.strip()
408
 
409
  return response_only
410
 
411
  except Exception as e:
412
+ st.error(f"{t('text_analysis_failed_error')} {e}")
413
  return None
414
 
415
+ # --- Interface Utilisateur Streamlit ---
416
  st.title(t("title"))
417
  st.markdown(t("subtitle"))
418
 
419
+ # --- Barre Latérale (Sidebar) pour la Configuration ---
420
  with st.sidebar:
421
  st.header(t("config_title"))
422
 
423
+ # Sélection de la langue
424
  lang_selector_options = ["Français", "English"]
425
  current_lang_index = 0 if st.session_state.language == "fr" else 1
426
  language_selected = st.selectbox(
 
433
 
434
  st.divider()
435
 
436
+ # Statut du Modèle IA et Bouton de chargement
437
  st.header(t("model_status"))
 
438
  if st.session_state.model_loaded and check_model_health():
439
+ st.success(t("model_status_loaded"))
440
+ st.write(f"**{t('model_type_label')}** `{st.session_state.model_status}`")
441
  if st.session_state.model_load_time:
442
  load_time_str = time.strftime('%H:%M:%S', time.localtime(st.session_state.model_load_time))
443
+ st.write(f"**{t('load_time_label')}** `{load_time_str}`")
444
 
445
+ if st.button(t("reload_model_button"), type="secondary"):
446
  st.session_state.model_loaded = False
447
  st.session_state.model = None
448
  st.session_state.processor = None
449
  st.session_state.model_status = "Non chargé"
450
  st.session_state.load_attempt_count = 0
451
+ st.info(t("model_unloaded_info"))
452
+ st.rerun() # Recharge l'application pour appliquer le changement
453
  else:
454
+ st.warning(t("model_status_not_loaded"))
455
 
456
  if st.button(t("load_model"), type="primary"):
457
+ with st.spinner(t("loading_model_spinner")):
458
+ # Utilise la fonction cachée pour charger le modèle
459
+ model, processor = load_model_and_processor()
460
+ if model is not None and processor is not None:
461
+ st.session_state.model = model
462
+ st.session_state.processor = processor
463
+ st.success(t("✅ Modèle IA chargé avec succès !")) # Message plus clair
464
  else:
465
+ st.error(t("❌ Échec du chargement du modèle IA."))
466
+ st.rerun() # Recharge pour mettre à jour l'UI
467
 
468
  st.divider()
469
+ # Affichage des ressources système
470
+ st.subheader(t("system_info_title"))
471
  afficher_ram_disponible()
472
  if torch.cuda.is_available():
473
  try:
474
  gpu_memory = torch.cuda.memory_allocated(0) / (1024**3)
475
  gpu_total = torch.cuda.get_device_properties(0).total_memory / (1024**3)
476
+ st.write(f"🚀 {t('sys_gpu_detected')}: {torch.cuda.get_device_name(0)}")
477
+ st.write(f"📊 {t('sys_gpu_total_mem')}: {gpu_total:.1f} GB")
478
  except Exception as e:
479
+ st.write(f"🚀 {t('sys_gpu_detected')}: Erreur lors de la récupération des informations GPU : {e}")
480
  else:
481
+ st.write(f"🚀 {t('sys_gpu_not_available')}")
482
 
483
+ # --- Onglets Principaux ---
484
  tab1, tab2, tab3, tab4 = st.tabs(t("tabs"))
485
 
486
  with tab1:
 
488
  st.markdown(t("image_analysis_desc"))
489
 
490
  capture_option = st.radio(
491
+ t("choose_capture_method_label"),
492
+ [t("upload_image_label"), t("webcam_capture_label")],
 
493
  horizontal=True,
494
  key="image_capture_method"
495
  )
 
497
  uploaded_file = None
498
  captured_image = None
499
 
500
+ if capture_option == t("upload_image_label"):
501
  uploaded_file = st.file_uploader(
502
  t("choose_image"),
503
  type=['png', 'jpg', 'jpeg'],
504
+ help="Formats acceptés : PNG, JPG, JPEG (taille max : 10MB).",
505
+ max_upload_size=10 # Limite à 10MB
506
  )
507
  if uploaded_file is not None and uploaded_file.size > 10 * 1024 * 1024:
508
+ st.warning(t("file_size_warning"))
509
+ else: # Webcam Capture
510
+ st.markdown(t("webcam_instructions"))
511
+ captured_image = st.camera_input(t("take_photo_label")) # Ajout d'une étiquette pour la caméra
 
512
 
513
  image_to_analyze = None
514
  if uploaded_file is not None:
515
  try:
516
  image_to_analyze = Image.open(uploaded_file)
517
  except Exception as e:
518
+ st.error(f"{t('image_processing_error')} {e}")
519
  elif captured_image is not None:
520
  try:
521
  image_to_analyze = Image.open(captured_image)
522
  except Exception as e:
523
+ st.error(f"{t('image_capture_processing_error')} {e}")
524
 
525
  if image_to_analyze is not None:
 
526
  resized_image, was_resized = resize_image_if_needed(image_to_analyze)
527
 
528
  col1, col2 = st.columns([1, 1])
529
  with col1:
530
+ st.image(resized_image, caption=t("image_to_analyze_caption"), use_container_width=True) # Caption pour l'image
531
  if was_resized:
532
+ st.info(t("image_resized_info").format(resized_image.size)) # Utilise la clé pour le message
533
 
534
  with col2:
535
  if st.session_state.model_loaded and check_model_health():
536
+ st.subheader(t("image_analysis_options_title"))
537
+
538
+ # Définition des options d'analyse basée sur la langue
539
+ analysis_options = [
540
+ t("analysis_type_full_diagnosis"),
541
+ t("analysis_type_disease_id"),
542
+ t("analysis_type_care_advice")
543
+ ]
544
  analysis_type = st.selectbox(
545
+ t("analysis_type_label"), # Ajout d'une clé pour le label du selectbox
546
+ analysis_options,
 
 
 
 
547
  key="image_analysis_type_selector"
548
  )
549
 
550
  custom_prompt_input = st.text_area(
551
+ t("custom_prompt_label"),
552
  value="",
553
  height=100,
554
+ placeholder=t("custom_prompt_placeholder") # Placeholder pour le prompt
555
  )
556
 
557
+ if st.button(t("analyze_image_button"), type="primary", key="analyze_image_button"):
558
  final_prompt = custom_prompt_input.strip()
559
+ # Utilise la fonction `t` pour obtenir le prompt par défaut basé sur la langue
560
  if not final_prompt:
561
+ if analysis_type == t("analysis_type_full_diagnosis"):
562
+ default_prompt_key = "default_prompt_full_diagnosis"
563
+ elif analysis_type == t("analysis_type_disease_id"):
564
+ default_prompt_key = "default_prompt_disease_id"
565
+ else: # Care Advice
566
+ default_prompt_key = "default_prompt_care_advice"
567
+
568
+ # Fonction pour obtenir les prompts par défaut en fonction de la langue
569
+ def get_default_prompt(lang, key):
570
+ prompts = {
571
+ "fr": {
572
+ "default_prompt_full_diagnosis": "<image>\nAnalyse cette image de plante et fournis un diagnostic complet :\n1. **État général de la plante :** Décris son apparence globale et sa vitalité.\n2. **Identification des problèmes :** Liste les maladies, parasites ou carences visibles.\n3. **Diagnostic probable :** Indique la maladie ou le problème le plus probable.\n4. **Causes possibles :** Explique ce qui a pu causer ce problème.\n5. **Recommandations de traitement :** Propose des solutions concrètes et adaptées.\n6. **Conseils préventifs :** Donne des astuces pour éviter que le problème ne revienne.\nRéponds de manière structurée et claire en français.",
573
+ "default_prompt_disease_id": "<image>\nDiagnostique cette plante en te concentrant sur les maladies et parasites :\n1. Identifie les symptômes visuels spécifiques aux maladies ou parasites.\n2. Détermine la maladie ou le parasite le plus probable.\n3. Explique les conditions favorisant leur développement.\n4. Propose des traitements ciblés et des méthodes de lutte.\nRéponds en français de manière structurée.",
574
+ "default_prompt_care_advice": "<image>\nAnalyse cette plante et donne des conseils de soins détaillés :\n1. État général de la plante : Évalue sa santé actuelle.\n2. Besoins spécifiques : Précise ses besoins en eau, lumière, nutriments et substrat.\n3. Conseils d'entretien : Donne des instructions pour l'arrosage, la fertilisation et la taille.\n4. Améliorations recommandées : Suggère des actions pour optimiser sa croissance et sa santé.\nRéponds en français de manière structurée."
575
+ },
576
+ "en": {
577
+ "default_prompt_full_diagnosis": "<image>\nAnalyze this plant image and provide a comprehensive diagnosis:\n1. **Overall Plant Health:** Describe its general appearance and vitality.\n2. **Problem Identification:** List visible diseases, pests, or deficiencies.\n3. **Probable Diagnosis:** Indicate the most likely disease or problem.\n4. **Possible Causes:** Explain what might have caused this issue.\n5. **Treatment Recommendations:** Propose concrete and appropriate solutions.\n6. **Preventive Advice:** Give tips to avoid the problem from recurring.\nRespond in a structured and clear manner in English.",
578
+ "default_prompt_disease_id": "<image>\nDiagnose this plant focusing on diseases and pests:\n1. Identify visual symptoms specific to diseases or pests.\n2. Determine the most likely disease or pest.\n3. Explain the conditions that favor their development.\n4. Propose targeted treatments and control methods.\nRespond in a structured manner in English.",
579
+ "default_prompt_care_advice": "<image>\nAnalyze this plant and provide detailed care advice:\n1. Overall plant condition: Assess its current health.\n2. Specific needs: Specify its requirements for water, light, nutrients, and substrate.\n3. Maintenance tips: Give instructions for watering, fertilizing, and pruning.\n4. Recommended improvements: Suggest actions to optimize its growth and health.\nRespond in a structured manner in English."
580
+ }
581
+ }
582
+ return prompts[lang].get(key, key) # Retourne la clé si non trouvée
583
+
584
+ final_prompt = get_default_prompt(st.session_state.language, default_prompt_key)
585
 
586
  analysis_result = analyze_image_multilingual(resized_image, prompt_text=final_prompt)
587
 
588
  if analysis_result:
589
+ st.success(t("analysis_complete_success"))
590
+ st.markdown(f"### {t('results_title')}")
591
  st.markdown(analysis_result)
592
  else:
593
+ st.error(t("analysis_failed_error"))
594
  else:
595
+ st.warning(t("model_not_loaded_warning"))
596
 
597
  with tab2:
598
  st.header(t("text_analysis_title"))
 
601
  text_description_input = st.text_area(
602
  t("enter_description"),
603
  height=200,
604
+ placeholder=t("enter_description_placeholder") # Placeholder pour la description
605
  )
606
 
607
+ if st.button(t("analyze_text_button"), type="primary", key="analyze_text_button"):
608
  if text_description_input.strip():
609
  if st.session_state.model_loaded and check_model_health():
610
  analysis_result = analyze_text_multilingual(text_description_input)
611
 
612
  if analysis_result:
613
+ st.success(t("analysis_complete_success"))
614
+ st.markdown(f"### {t('results_title')}")
615
  st.markdown(analysis_result)
616
  else:
617
+ st.error(t("text_analysis_failed_error"))
618
  else:
619
+ st.warning(t("model_not_loaded_warning"))
620
  else:
621
+ st.warning(t("please_enter_description_warning")) # Avertissement si pas de description
622
 
623
  with tab3:
624
  st.header(t("config_title"))
 
626
  col1, col2 = st.columns(2)
627
 
628
  with col1:
629
+ st.subheader(t("system_info_title"))
 
630
  try:
631
  ram = psutil.virtual_memory()
632
+ st.write(f"**{t('sys_ram_total')}** {ram.total / (1024**3):.1f} GB")
633
+ st.write(f"**{t('sys_ram_used')}** {ram.used / (1024**3):.1f} GB ({ram.percent:.1f}%)")
634
 
635
  disk = psutil.disk_usage('/')
636
+ st.write(f"**{t('sys_disk_free')}** {disk.free / (1024**3):.1f} GB")
637
 
638
  if torch.cuda.is_available():
639
+ st.write(f"**{t('sys_gpu_detected')}** {torch.cuda.get_device_name(0)}")
640
  gpu_props = torch.cuda.get_device_properties(0)
641
+ st.write(f"**{t('sys_gpu_total_mem')}** {gpu_props.total_memory / (1024**3):.1f} GB")
642
  else:
643
+ st.write(f"**{t('sys_gpu_not_available')}**")
644
  except Exception as e:
645
+ st.error(f"{t('sys_info_error')} {e}")
646
 
647
  with col2:
648
+ st.subheader(t("model_stats_title"))
 
649
  if st.session_state.model_loaded and check_model_health():
650
+ st.write(f"**{t('model_status')}:** {t('model_status_loaded')}")
651
  if st.session_state.model is not None:
652
+ st.write(f"**{t('model_type_label')}** `{type(st.session_state.model).__name__}`")
653
  if hasattr(st.session_state.model, 'device'):
654
+ st.write(f"**{t('device_used_label')}** `{st.session_state.model.device}`")
655
  if st.session_state.model_load_time:
656
  load_time_str = time.strftime('%H:%M:%S', time.localtime(st.session_state.model_load_time))
657
+ st.write(f"**{t('load_time_label')}** `{load_time_str}`")
658
  else:
659
+ st.write(f"**{t('model_status')}:** {t('model_status_not_loaded')}")
660
+ st.write(f"**{t('model_type_label')}** N/A")
661
+ st.write(f"**{t('device_used_label')}** N/A")
662
 
663
  with tab4:
664
  st.header(t("about_title"))
 
665
  st.markdown("""
666
  ## 🌱 AgriLens AI : Votre Assistant d'Analyse de Plantes
667
  **AgriLens AI** est une application alimentée par l'intelligence artificielle qui vous aide à identifier les problèmes de vos plantes, à diagnostiquer les maladies et à obtenir des conseils de traitement personnalisés. Que vous soyez un jardinier débutant ou expérimenté, AgriLens AI est là pour vous accompagner.
 
671
  - **Recommandations Précises :** Recevez des conseils de traitement, des suggestions de soins et des mesures préventives adaptées à chaque situation.
672
  - **Support Multilingue :** L'interface et les réponses sont disponibles en français et en anglais pour une accessibilité maximale.
673
  ### 🤖 Technologie Utilisée :
674
+ - **Modèle IA :** Google Gemma 3n E4B IT, un modèle multimodal performant pour l'analyse de plantes. ([Voir sur Hugging Face](https://huggingface.co/google/gemma-3n-E4B-it))
675
  - **Bibliothèques :** `transformers`, `torch`, `streamlit`, `Pillow`, `psutil`, `requests`, `huggingface-hub`.
676
  ### 📝 Comment Utiliser AgriLens AI :
677
+ 1. **Chargez le Modèle IA :** Dans la barre latérale, cliquez sur "Charger le Modèle IA". Attendez que le statut passe à "Chargé et fonctionnel". Cela peut prendre quelques minutes.
678
  2. **Analysez votre Plante :**
679
  - **Via Image :** Allez à l'onglet "📸 Analyse d'Image". Uploadez une photo de votre plante ou utilisez votre webcam. Choisissez le type d'analyse et cliquez sur "Analyser l'image".
680
  - **Via Texte :** Allez à l'onglet "📝 Analyse de Texte". Décrivez les symptômes de votre plante dans la zone de texte et cliquez sur "Analyser la description".
 
686
  *Développé avec passion pour aider les jardiniers et amoureux des plantes à prendre soin de leurs cultures grâce à la puissance de l'IA.*
687
  """)
688
 
689
+ # --- Pied de page ---
690
  st.divider()
691
  st.markdown("""
692
  <div style='text-align: center; color: #666;'>
693
+ 🌱 AgriLens AI - Assistant d'Analyse de Plantes |
694
+ <a href='#' target='_blank'>Documentation</a> |
695
+ <a href='#' target='_blank'>Support</a> |
696
+ <a href='https://huggingface.co/google/gemma-3n-E4B-it' target='_blank'>Modèle Gemma 3n</a>
697
  </div>
698
  """, unsafe_allow_html=True)