Spaces:
Runtime error
Runtime error
File size: 7,470 Bytes
315f3ab d852ed4 315f3ab d852ed4 d209343 d852ed4 315f3ab d852ed4 315f3ab d852ed4 315f3ab d209343 315f3ab d852ed4 315f3ab d209343 315f3ab d209343 315f3ab d209343 d852ed4 315f3ab d209343 315f3ab d209343 315f3ab d209343 315f3ab d209343 315f3ab d209343 ac34952 d209343 ac34952 d209343 ac34952 d209343 ac34952 d209343 ac34952 d209343 ac34952 d209343 ac34952 d209343 ac34952 d209343 315f3ab | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | import os
import gradio as gr
import torch
from PIL import Image
# Importation directe de la classe spécifique pour Qwen2.5-VL
from transformers import AutoProcessor, Qwen2_5_VLForConditionalGeneration
import spaces # Import crucial pour utiliser les GPU gratuits de Hugging Face
# --- CONFIGURATION ---
# REMPLACEZ 'votre_vrai_username' par votre véritable identifiant Hugging Face
MODEL_ID = "hamoud-7/schema-bac-qwen-model"
HF_TOKEN = os.environ.get("HF_TOKEN")
print("⏳ Chargement du processeur et du modèle de vision Qwen2.5-VL...")
processor = AutoProcessor.from_pretrained(MODEL_ID, token=HF_TOKEN)
model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
MODEL_ID,
torch_dtype=torch.float16,
device_map="auto",
token=HF_TOKEN
)
print("✅ Application prête !")
# --- FONCTION D'INFÉRENCE PROPULSÉE PAR ZEROGPU ---
@spaces.GPU # Active le GPU gratuitement à la demande pour l'utilisateur
def run_inference(image_pil, question_text):
if image_pil is None or not question_text.strip():
return "Erreur : Veuillez fournir une image ET une question."
# Préparation du prompt au format Qwen-VL
messages = [
{"role": "user", "content": [
{"type": "image", "image": image_pil},
{"type": "text", "text": question_text}
]}
]
# Application du template de chat standard
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
# Traitement des entrées pour le modèle (gère automatiquement les images et le texte)
inputs = processor(text=[text], images=[image_pil], padding=True, return_tensors="pt").to("cuda")
# Génération de la réponse
with torch.no_grad():
generated_ids = model.generate(**inputs, max_new_tokens=128, do_sample=False)
# Découpage des tokens générés pour ne garder que la réponse de l'assistant
generated_ids_trimmed = [out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]
response_text = processor.batch_decode(generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
return response_text.strip()
# --- INTERFACE DESIGN GRADIO ---
logo_url = "https://imgur.com/GDKnbVp.png"
custom_css = """
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
.gradio-container {
font-family: 'Poppins', sans-serif !important;
background: linear-gradient(135deg, #f5f7fb 0%, #eef1f8 100%) !important;
width: 100% !important;
max-width: 100% !important; /* Modifié pour occuper tout l'espace disponible */
margin: 0 auto !important;
padding: 0 10px !important;
}
/* --- En-tête --- */
#header-block {
background: linear-gradient(120deg, #4338CA 0%, #6366F1 55%, #4F46E5 100%);
border-radius: 22px;
padding: 34px 20px !important;
margin-bottom: 26px;
box-shadow: 0 12px 30px rgba(79, 70, 229, 0.28);
}
#header-block h1 {
color: #ffffff !important;
font-weight: 700 !important;
letter-spacing: 0.3px;
}
#header-block p {
color: #E0E7FF !important;
}
/* --- Cartes --- */
.card {
background: #ffffff !important;
border-radius: 18px !important;
padding: 22px !important;
box-shadow: 0 4px 22px rgba(15, 23, 42, 0.07) !important;
border: 1px solid #EEF2FF !important;
}
.card h3, .card .prose h3 {
margin-top: 0 !important;
color: #312E81 !important;
font-weight: 600 !important;
}
/* --- Bouton principal --- */
#submit-btn {
background: linear-gradient(120deg, #4F46E5, #6366F1) !important;
border: none !important;
color: #ffffff !important;
font-weight: 600 !important;
border-radius: 12px !important;
box-shadow: 0 6px 16px rgba(79, 70, 229, 0.35) !important;
transition: transform 0.15s ease, box-shadow 0.15s ease !important;
}
#submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 24px rgba(79, 70, 229, 0.45) !important;
}
/* --- Zone de réponse --- */
#output-box textarea {
background: #F8FAFC !important;
border-radius: 12px !important;
font-size: 1rem !important;
border: 1px solid #E2E8F0 !important;
}
/* --- Exemples --- */
#examples-card .gr-samples-table, #examples-card table {
border-radius: 12px !important;
overflow: hidden !important;
}
footer {visibility: hidden}
"""
with gr.Blocks(
theme=gr.themes.Soft(primary_hue="indigo", secondary_hue="blue"),
title="SCHÉMA BAC",
css=custom_css,
fill_width=True # Ajouté pour forcer l'interface à s'étaler horizontalement
) as demo:
with gr.Column(elem_id="header-block"):
gr.HTML(f'''
<div style="text-align: center;">
<img src="{logo_url}" alt="SCHÉMA BAC Logo" style="height: 70px; display: block; margin: 0 auto 12px auto;">
<h1 style="margin:0; font-size: 2.1rem;">SCHÉMA BAC</h1>
<p style="font-size: 1.05rem; margin-top: 8px; max-width: 640px; margin-left:auto; margin-right:auto;">
L'intelligence artificielle au service de la compréhension des schémas scientifiques.
</p>
</div>
''')
with gr.Row(equal_height=True):
with gr.Column(scale=1, min_width=320, elem_classes="card"): # min_width adapté pour maintenir la structure côte à côte
gr.Markdown("### 1️⃣ Téléchargez votre schéma")
image_input = gr.Image(type="pil", label="", height=280)
gr.Markdown("### 2️⃣ Posez votre question")
question_input = gr.Textbox(
label="",
placeholder="Ex : Quelle est la fonction de la structure numéro 3 ?",
lines=3
)
submit_btn = gr.Button("🔍 Analyser le schéma", variant="primary", elem_id="submit-btn")
with gr.Column(scale=1, min_width=320, elem_classes="card"): # min_width adapté pour maintenir la structure côte à côte
gr.Markdown("### 🤖 Analyse de l'assistant")
output_text = gr.Textbox(
label="",
interactive=False,
lines=14,
elem_id="output-box",
placeholder="La réponse de l'IA apparaîtra ici..."
)
with gr.Group(elem_classes="card", elem_id="examples-card"):
gr.Markdown("### 📚 Exemples à essayer — cliquez pour tester")
gr.Examples(
examples=[
[
# 1. On remplace "examples" par "images"
# 2. Changez "votre_image_1.jpg" par le nom exact de votre premier fichier
os.path.join("images", "image.jpg"),
"Quel est le rôle de la structure numéro 3 ?"
],
[
# Faites de même pour la deuxième image
os.path.join("images", "image (3).jpg"),
"Que représente la structure numéro 3 sur cette coupe de rein ?"
],
[
# Et pour la troisième image
os.path.join("images", "image (1).jpg"),
"Quelle est la fonction de la structure numéro 2 ?"
],
],
inputs=[image_input, question_input],
label=""
)
submit_btn.click(fn=run_inference, inputs=[image_input, question_input], outputs=output_text)
if __name__ == "__main__":
demo.launch() |