import streamlit as st import pandas as pd from pocketgroq import GroqProvider import time import json import uuid # Configuration de la page st.set_page_config( page_title="VisiPilot IFS Food 8", page_icon="🔍", layout="wide", initial_sidebar_state="expanded" ) # Styles CSS st.markdown(""" """, unsafe_allow_html=True) # Initialiser les états de session if 'api_key' not in st.session_state: st.session_state['api_key'] = "" if 'action_plan_df' not in st.session_state: st.session_state['action_plan_df'] = None if 'recommendations' not in st.session_state: st.session_state['recommendations'] = {} if 'responses' not in st.session_state: st.session_state['responses'] = {} if 'active_item' not in st.session_state: st.session_state['active_item'] = None if 'ask_questions' not in st.session_state: st.session_state['ask_questions'] = {} if 'session_id' not in st.session_state: st.session_state['session_id'] = str(uuid.uuid4()) # Initialiser GroqProvider def get_groq_provider(): if not st.session_state.get('api_key'): st.error("⚠️ Veuillez entrer votre clé API Groq.") return None return GroqProvider(api_key=st.session_state.api_key) # Charger le fichier Excel avec le plan d'action def load_action_plan(uploaded_file): try: # Utiliser header=11 pour sauter les lignes d'en-tête action_plan_df = pd.read_excel(uploaded_file, header=11) action_plan_df = action_plan_df[["requirementNo", "requirementText", "requirementExplanation"]] action_plan_df.columns = ["Numéro d'exigence", "Exigence IFS Food 8", "Explication (par l'auditeur/l'évaluateur)"] # Ajouter une colonne de statut action_plan_df["Statut"] = "Non traité" # Supprimer les lignes vides (où le numéro d'exigence est NaN ou vide) action_plan_df = action_plan_df.dropna(subset=["Numéro d'exigence"]) action_plan_df = action_plan_df[action_plan_df["Numéro d'exigence"].astype(str).str.strip() != ""] return action_plan_df except Exception as e: st.error(f"⚠️ Erreur lors de la lecture du fichier: {str(e)}") return None # Générer des questions adaptées à la non-conformité def generate_questions(non_conformity): req_text = non_conformity["Exigence IFS Food 8"] audit_comment = non_conformity["Explication (par l'auditeur/l'évaluateur)"] # Détecter les mots-clés pour personnaliser les questions keywords = { "formation": ["formation", "compétence", "qualification", "personnel"], "documentation": ["document", "procédure", "enregistrement", "contrôle"], "équipement": ["équipement", "matériel", "maintenance", "installation"], "hygiène": ["hygiène", "nettoyage", "désinfection", "contamination"], "traçabilité": ["traçabilité", "lot", "identification", "rappel"] } # Questions de base questions = [ { "id": "context", "question": "Décrivez brièvement le contexte actuel lié à cette exigence dans votre entreprise." }, { "id": "cause", "question": "Selon vous, quelle est la cause principale de cette non-conformité ?" } ] # Ajouter des questions spécifiques en fonction des mots-clés for category, terms in keywords.items(): for term in terms: if term.lower() in req_text.lower() or term.lower() in audit_comment.lower(): if category == "formation": questions.append({ "id": "training", "question": "Le personnel a-t-il reçu une formation spécifique sur ce sujet ? Si oui, quand était la dernière formation ?" }) break elif category == "documentation": questions.append({ "id": "documentation", "question": "Disposez-vous d'une procédure ou d'instructions pour ce processus ? Est-elle à jour ?" }) break elif category == "équipement": questions.append({ "id": "equipment", "question": "Les équipements concernés sont-ils adaptés et correctement entretenus ?" }) break elif category == "hygiène": questions.append({ "id": "hygiene", "question": "Quelles sont vos pratiques actuelles de nettoyage/désinfection dans cette zone ?" }) break elif category == "traçabilité": questions.append({ "id": "traceability", "question": "Comment assurez-vous actuellement la traçabilité dans ce processus ?" }) break # Limiter à 3 questions maximum return questions[:3] # Détecter la langue du texte def detect_language(text): # Liste de mots français courants pour détection simple french_words = ["et", "les", "des", "dans", "pour", "avec", "par", "sur", "en", "au", "aux", "de", "la", "le", "du", "un", "une", "cette", "est", "sont", "ont", "qui", "non", "conformité", "exigence", "auditeur"] # Compter les mots français text_lower = text.lower() french_count = sum(1 for word in french_words if f" {word} " in f" {text_lower} ") # Si au moins 3 mots français sont détectés, considérer comme français return "fr" if french_count >= 3 else "en" # Générer une recommandation avec Groq def generate_ai_recommendation(non_conformity, responses=None, direct=False): groq = get_groq_provider() if not groq: return "Erreur: clé API non fournie." # Détecter la langue language = detect_language(non_conformity["Exigence IFS Food 8"] + " " + non_conformity["Explication (par l'auditeur/l'évaluateur)"]) # Construire le prompt selon la langue détectée if language == "fr": if direct: prompt = f""" En tant qu'expert en IFS Food 8 et en sécurité alimentaire, analysez cette non-conformité et proposez un plan d'action complet. # NON-CONFORMITÉ - Exigence N°: {non_conformity["Numéro d'exigence"]} - Exigence IFS Food 8: {non_conformity["Exigence IFS Food 8"]} - Constat de l'auditeur: {non_conformity["Explication (par l'auditeur/l'évaluateur)"]} # VOTRE MISSION Fournir une analyse et un plan d'action structuré avec les sections suivantes: 1. ANALYSE DE LA NON-CONFORMITÉ Analysez la situation et identifiez clairement le problème. 2. ANALYSE DES CAUSES Identifiez et détaillez les causes racines probables. 3. PLAN D'ACTION a) Actions immédiates (corrections) b) Type de preuves à fournir c) Actions correctives à long terme 4. MÉTHODES DE VALIDATION DE L'EFFICACITÉ Comment vérifier que les actions mises en place sont efficaces. 5. RECOMMANDATIONS COMPLÉMENTAIRES Rédigez l'ensemble de l'analyse en français et fournissez des recommandations spécifiques, réalistes et conformes à l'IFS Food 8. """ else: prompt = f""" En tant qu'expert en IFS Food 8 et en sécurité alimentaire, analysez cette non-conformité et les informations fournies pour proposer un plan d'action adapté. # NON-CONFORMITÉ - Exigence N°: {non_conformity["Numéro d'exigence"]} - Exigence IFS Food 8: {non_conformity["Exigence IFS Food 8"]} - Constat de l'auditeur: {non_conformity["Explication (par l'auditeur/l'évaluateur)"]} # INFORMATIONS FOURNIES PAR L'UTILISATEUR {json.dumps(responses, indent=2)} # VOTRE MISSION Fournir une analyse et un plan d'action structuré avec les sections suivantes: 1. ANALYSE DE LA NON-CONFORMITÉ Analysez la situation et identifiez clairement le problème en tenant compte des informations fournies. 2. ANALYSE DES CAUSES Identifiez et détaillez les causes racines probables. 3. PLAN D'ACTION a) Actions immédiates (corrections) b) Type de preuves à fournir c) Actions correctives à long terme 4. MÉTHODES DE VALIDATION DE L'EFFICACITÉ Comment vérifier que les actions mises en place sont efficaces. 5. RECOMMANDATIONS COMPLÉMENTAIRES Rédigez l'ensemble de l'analyse en français et fournissez des recommandations spécifiques, réalistes et conformes à l'IFS Food 8. """ else: # En anglais if direct: prompt = f""" As an IFS Food 8 and food safety expert, analyze this non-conformity and provide a comprehensive action plan. # NON-CONFORMITY - Requirement No.: {non_conformity["Numéro d'exigence"]} - IFS Food 8 Requirement: {non_conformity["Exigence IFS Food 8"]} - Auditor's finding: {non_conformity["Explication (par l'auditeur/l'évaluateur)"]} # YOUR MISSION Provide an analysis and structured action plan with the following sections: 1. ANALYSIS OF THE NON-CONFORMITY Analyze the situation and clearly identify the problem. 2. ROOT CAUSE ANALYSIS Identify and detail the probable root causes. 3. ACTION PLAN a) Immediate actions (corrections) b) Type of evidence to be provided c) Long-term corrective actions 4. METHODS FOR VALIDATING EFFECTIVENESS How to verify that the implemented actions are effective. 5. ADDITIONAL RECOMMENDATIONS Write the entire analysis in English and provide specific, realistic recommendations that comply with IFS Food 8. """ else: prompt = f""" As an IFS Food 8 and food safety expert, analyze this non-conformity and the information provided to propose an adapted action plan. # NON-CONFORMITY - Requirement No.: {non_conformity["Numéro d'exigence"]} - IFS Food 8 Requirement: {non_conformity["Exigence IFS Food 8"]} - Auditor's finding: {non_conformity["Explication (par l'auditeur/l'évaluateur)"]} # INFORMATION PROVIDED BY THE USER {json.dumps(responses, indent=2)} # YOUR MISSION Provide an analysis and structured action plan with the following sections: 1. ANALYSIS OF THE NON-CONFORMITY Analyze the situation and clearly identify the problem, taking into account the information provided. 2. ROOT CAUSE ANALYSIS Identify and detail the probable root causes. 3. ACTION PLAN a) Immediate actions (corrections) b) Type of evidence to be provided c) Long-term corrective actions 4. METHODS FOR VALIDATING EFFECTIVENESS How to verify that the implemented actions are effective. 5. ADDITIONAL RECOMMENDATIONS Write the entire analysis in English and provide specific, realistic recommendations that comply with IFS Food 8. """ try: # Option: Utilisez Chain of Thought pour une analyse plus approfondie return groq.generate(prompt, max_tokens=1500, temperature=0.2, use_cot=True) except Exception as e: st.error(f"⚠️ Erreur lors de la génération de la recommandation : {str(e)}") return None # Sauvegarde et chargement de session def save_session_data(): session_data = { "recommendations": st.session_state['recommendations'], "responses": st.session_state['responses'], "action_plan_status": st.session_state['action_plan_df']["Statut"].to_dict() if st.session_state['action_plan_df'] is not None else {} } return json.dumps(session_data) def load_session_data(session_json): try: data = json.loads(session_json) st.session_state['recommendations'] = data.get("recommendations", {}) st.session_state['responses'] = data.get("responses", {}) # Mettre à jour les statuts si le plan d'action est déjà chargé if st.session_state['action_plan_df'] is not None and "action_plan_status" in data: for idx, status in data["action_plan_status"].items(): if int(idx) in st.session_state['action_plan_df'].index: st.session_state['action_plan_df'].loc[int(idx), "Statut"] = status return True except Exception as e: st.error(f"⚠️ Erreur lors du chargement de la session : {str(e)}") return False # Interface principale def main(): # Ajouter la bannière VisiPilot st.markdown('
', unsafe_allow_html=True) st.markdown('