PLGI / app.py
odilegx's picture
Update app.py
aa85132 verified
Raw
History Blame Contribute Delete
5.8 kB
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="<strong>Bienvenue !</strong> <BR> 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()