import os import gradio as gr from google import genai from google.genai import types # Initialisation du client Gemini avec la nouvelle bibliothèque officielle api_key = os.environ.get("GEMINI_API_KEY") client = genai.Client(api_key=api_key) # Chargement sécurisé de la base de connaissances textuelle try: with open("connaissances.txt", "r", encoding="utf-8") as f: connaissances = f.read() except FileNotFoundError: connaissances = "Aucun document de référence n'a été trouvé." # Définition précise du comportement socratique, neutre et professionnel SYSTEM_INSTRUCTION = f"""Tu es un assistant pédagogique universitaire, expert et factuel. Ton ton doit être strictement professionnel, direct et neutre. Évite absolument toute forme de paternalisme, de familiarité excessive ou de flatterie moralisatrice (pas de "C'est bien mon grand", "Bravo", "Tu y es presque"). Base de connaissances fournie (retranscriptions de cours) : {connaissances} RÈGLE DE DÉMARRAGE ET USAGE DU PRÉNOM : 1. Lors du tout premier message de l'étudiant, s'il ne s'est pas présenté, demande-lui brièvement son prénom de manière courtoise pour la session. 2. Une fois le prénom donné, salue-le sobrement. 3. RESTRICTION STRICTE : Tu ne dois utiliser son prénom que TRÈS RAREMENT (maximum une fois toutes les 4 ou 5 interactions), uniquement si cela apporte de la clarté, et jamais pour introduire une réprimande ou une félicitation. Règles de guidage socratique (Stricte conformité) : 1. Ne donne jamais la solution finale, ni de listes d'actions complètes. 2. Analyse le blocage de l'étudiant uniquement par rapport aux retranscriptions fournies. 3. Formule une seule question precise ou un seul indice technique à la fois. 4. Reste extrêmement concis : tes réponses doivent aller droit au but et faire 2 phrases maximum. 5. Si la demande sort du cadre des retranscriptions, indique-le de manière factuelle sans fioriture.""" def tuteur_response(message, history): contents = [] # Nettoyage rigoureux de l'historique pour l'API Google for interaction in history: if isinstance(interaction, dict): role = "user" if interaction.get("role") == "user" else "model" content_data = interaction.get("content", "") if isinstance(content_data, list) and len(content_data) > 0: text_content = content_data[0].get("text", "") if isinstance(content_data[0], dict) else str(content_data[0]) elif isinstance(content_data, dict): text_content = content_data.get("text", "") else: text_content = str(content_data) contents.append({"role": role, "parts": [{"text": text_content}]}) elif hasattr(interaction, "role") and hasattr(interaction, "content"): role = "user" if interaction.role == "user" else "model" content_data = interaction.content if isinstance(content_data, list) and len(content_data) > 0: text_content = content_data[0].get("text", "") if isinstance(content_data[0], dict) else str(content_data[0]) else: text_content = str(content_data) contents.append({"role": role, "parts": [{"text": text_content}]}) elif isinstance(interaction, (list, tuple)) and len(interaction) == 2: try: user_msg, bot_msg = interaction if isinstance(user_msg, dict): user_msg = user_msg.get("text", str(user_msg)) if isinstance(bot_msg, dict): bot_msg = bot_msg.get("text", str(bot_msg)) contents.append({"role": "user", "parts": [{"text": str(user_msg)}]}) contents.append({"role": "model", "parts": [{"text": str(bot_msg)}]}) except Exception: pass # Ajout du dernier message de l'étudiant clean_message = message.get("text", str(message)) if isinstance(message, dict) else str(message) contents.append({"role": "user", "parts": [{"text": clean_message}]}) try: response = client.models.generate_content( model='gemini-2.5-flash', contents=contents, config=types.GenerateContentConfig( system_instruction=SYSTEM_INSTRUCTION, temperature=0.7 ), ) return response.text except Exception as e: return f"Une erreur technique est survenue : {str(e)}" # Nouveau CSS pour encadrer proprement le bloc complet du prompt (champ + bouton) custom_css = """ footer {visibility: hidden !important;} /* Encadre tout le bloc inférieur (boite de saisie + bouton envoyer inclus) */ .form { border: 3px solid #10b981 !important; border-radius: 12px !important; background-color: #f8fafc !important; padding: 6px !important; box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15) !important; } /* Supprime les bordures internes par défaut pour éviter l'effet "double cadre" */ .form token-box, .form textarea { border: none !important; box-shadow: none !important; } """ # Définition du thème custom_theme = gr.themes.Soft(primary_hue="emerald", neutral_hue="slate") # Création de l'interface with gr.Blocks(theme=custom_theme, css=custom_css) as demo: gr.ChatInterface( fn=tuteur_response, title="Assistant virtuel", description="Bienvenue !
Attention ceci est une version en test.", # Ajout de height=400 pour réduire la taille verticale de la fenêtre de discussion chatbot=gr.Chatbot(placeholder="Posez vos questions sur les exercices pratiques ci-dessous.", height=400), textbox=gr.Textbox(scale=7) ) if __name__ == "__main__": demo.launch()