|
import gradio as gr |
|
from transformers import AutoTokenizer, AutoModelForCausalLM |
|
from peft import PeftModel, PeftConfig |
|
import torch |
|
import re |
|
|
|
class MoroccanStudentChatbot: |
|
def __init__(self, adapter_model_id="echarif/llama_alpaca_lora_adapter"): |
|
"""Initialize the chatbot with the fine-tuned PEFT/LoRA model""" |
|
print("Loading PEFT adapter config...") |
|
self.config = PeftConfig.from_pretrained(adapter_model_id) |
|
|
|
print("Loading base model...") |
|
self.base_model = AutoModelForCausalLM.from_pretrained( |
|
self.config.base_model_name_or_path, |
|
return_dict=True, |
|
device_map="auto", |
|
torch_dtype=torch.float16 |
|
) |
|
|
|
print("Loading LoRA adapter...") |
|
self.model = PeftModel.from_pretrained(self.base_model, adapter_model_id) |
|
|
|
print("Loading tokenizer...") |
|
self.tokenizer = AutoTokenizer.from_pretrained(self.config.base_model_name_or_path) |
|
|
|
|
|
if self.tokenizer.pad_token is None: |
|
self.tokenizer.pad_token = self.tokenizer.eos_token |
|
|
|
self.alpaca_prompt = """Ci-dessous se trouve une instruction décrivant une tâche, accompagnée éventuellement d'un contexte supplémentaire. Rédige une réponse qui complète correctement la demande. |
|
### Instruction : |
|
{} |
|
### input : |
|
{} |
|
### output : |
|
{}""" |
|
|
|
print("Model loaded successfully!") |
|
|
|
def clean_output(self, raw_output): |
|
"""Clean the model output to remove unwanted tokens and formatting""" |
|
|
|
if isinstance(raw_output, torch.Tensor): |
|
text = self.tokenizer.decode(raw_output[0], skip_special_tokens=True) |
|
else: |
|
text = raw_output |
|
|
|
|
|
|
|
output_pattern = r"### output :\s*(.*?)(?:<\|end_of_text\|>|$)" |
|
match = re.search(output_pattern, text, re.DOTALL) |
|
|
|
if match: |
|
response = match.group(1).strip() |
|
else: |
|
|
|
if "### output :" in text: |
|
response = text.split("### output :")[1].strip() |
|
else: |
|
response = text |
|
|
|
|
|
response = re.sub(r'<\|.*?\|>', '', response) |
|
response = re.sub(r'<.*?>', '', response) |
|
|
|
|
|
response = re.sub(r'\n+', '\n', response) |
|
response = response.strip() |
|
|
|
return response if response else "Je suis désolé, je n'ai pas pu générer une réponse appropriée." |
|
|
|
def generate_response(self, user_input, history): |
|
"""Generate response for the chatbot""" |
|
if not user_input.strip(): |
|
return history, "" |
|
|
|
|
|
prompt = self.alpaca_prompt.format(user_input.strip(), "", "") |
|
|
|
|
|
inputs = self.tokenizer(prompt, return_tensors="pt", padding=True, truncation=True, max_length=512) |
|
inputs = {k: v.to(self.model.device) for k, v in inputs.items()} |
|
|
|
|
|
with torch.no_grad(): |
|
outputs = self.model.generate( |
|
**inputs, |
|
max_new_tokens=256, |
|
do_sample=True, |
|
temperature=0.7, |
|
top_p=0.9, |
|
pad_token_id=self.tokenizer.eos_token_id, |
|
repetition_penalty=1.1, |
|
) |
|
|
|
|
|
response = self.clean_output(outputs) |
|
|
|
|
|
history.append([user_input, response]) |
|
|
|
return history, "" |
|
|
|
def create_interface(): |
|
"""Create the Gradio interface""" |
|
|
|
|
|
chatbot = MoroccanStudentChatbot() |
|
|
|
|
|
custom_css = """ |
|
.header-container { |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: center; |
|
padding: 20px; |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
border-radius: 10px; |
|
margin-bottom: 20px; |
|
color: white; |
|
} |
|
|
|
.logo-container { |
|
display: flex; |
|
align-items: center; |
|
gap: 15px; |
|
} |
|
|
|
.logo-placeholder { |
|
width: 60px; |
|
height: 60px; |
|
background: rgba(255, 255, 255, 0.2); |
|
border-radius: 10px; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
font-size: 24px; |
|
border: 2px solid rgba(255, 255, 255, 0.3); |
|
} |
|
|
|
.title-section h1 { |
|
margin: 0; |
|
font-size: 24px; |
|
font-weight: bold; |
|
} |
|
|
|
.title-section p { |
|
margin: 5px 0 0 0; |
|
font-size: 14px; |
|
opacity: 0.9; |
|
} |
|
|
|
.university-info { |
|
text-align: right; |
|
font-size: 12px; |
|
opacity: 0.8; |
|
} |
|
|
|
/* Responsive design */ |
|
@media (max-width: 768px) { |
|
.header-container { |
|
flex-direction: column; |
|
text-align: center; |
|
gap: 15px; |
|
} |
|
|
|
.university-info { |
|
text-align: center; |
|
} |
|
|
|
.title-section h1 { |
|
font-size: 20px; |
|
} |
|
|
|
.logo-placeholder { |
|
width: 50px; |
|
height: 50px; |
|
font-size: 20px; |
|
} |
|
} |
|
|
|
.chatbot-container { |
|
max-width: 800px; |
|
margin: 0 auto; |
|
} |
|
|
|
.footer-info { |
|
text-align: center; |
|
margin-top: 20px; |
|
padding: 15px; |
|
background: #f8f9fa; |
|
border-radius: 8px; |
|
font-size: 12px; |
|
color: #666; |
|
} |
|
|
|
/* Custom chatbot styling */ |
|
.gradio-container { |
|
max-width: 1200px !important; |
|
} |
|
|
|
/* Improve mobile responsiveness */ |
|
@media (max-width: 480px) { |
|
.header-container { |
|
padding: 15px; |
|
} |
|
|
|
.title-section h1 { |
|
font-size: 18px; |
|
} |
|
|
|
.logo-placeholder { |
|
width: 40px; |
|
height: 40px; |
|
font-size: 16px; |
|
} |
|
} |
|
""" |
|
|
|
|
|
with gr.Blocks(css=custom_css, title="Assistant Étudiant Marocain", theme=gr.themes.Soft()) as interface: |
|
|
|
|
|
gr.HTML(""" |
|
<div class="header-container"> |
|
<div class="logo-container"> |
|
<div class="logo-placeholder">🎓</div> |
|
<div class="title-section"> |
|
<h1>Assistant Étudiant Marocain</h1> |
|
<p>Votre guide pour l'enseignement supérieur au Maroc</p> |
|
</div> |
|
</div> |
|
<div class="university-info"> |
|
<div class="logo-placeholder">🏛️</div> |
|
<div style="margin-top: 5px;"> |
|
<strong>Université [Nom de votre université]</strong><br> |
|
Master [Votre spécialité]<br> |
|
Projet de fin d'études |
|
</div> |
|
</div> |
|
</div> |
|
""") |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
|
|
chatbot_interface = gr.Chatbot( |
|
height=500, |
|
placeholder="👋 Bonjour! Je suis votre assistant pour l'enseignement supérieur au Maroc. Posez-moi vos questions sur les universités, les inscriptions, les bourses, etc.", |
|
container=True, |
|
bubble_full_width=False, |
|
show_label=False, |
|
elem_classes="chatbot-container" |
|
) |
|
|
|
with gr.Row(): |
|
msg = gr.Textbox( |
|
placeholder="Tapez votre question ici...", |
|
container=False, |
|
scale=7, |
|
min_width=0, |
|
) |
|
send_btn = gr.Button("Envoyer", variant="primary", scale=1, min_width=0) |
|
|
|
|
|
clear_btn = gr.Button("Nouvelle conversation", variant="secondary", size="sm") |
|
|
|
|
|
gr.HTML(""" |
|
<div style="margin-top: 20px;"> |
|
<h3>💡 Questions d'exemple :</h3> |
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 10px; margin-top: 10px;"> |
|
<button onclick="document.querySelector('textarea').value='Quelles sont les meilleures universités pour étudier l\\'informatique au Maroc?'; document.querySelector('textarea').focus();" |
|
style="padding: 10px; border: 1px solid #ddd; border-radius: 5px; background: #f8f9fa; cursor: pointer;"> |
|
🏫 Universités d'informatique |
|
</button> |
|
<button onclick="document.querySelector('textarea').value='Comment obtenir une bourse d\\'études au Maroc?'; document.querySelector('textarea').focus();" |
|
style="padding: 10px; border: 1px solid #ddd; border-radius: 5px; background: #f8f9fa; cursor: pointer;"> |
|
💰 Bourses d'études |
|
</button> |
|
<button onclick="document.querySelector('textarea').value='Quelles sont les procédures d\\'inscription dans les universités publiques?'; document.querySelector('textarea').focus();" |
|
style="padding: 10px; border: 1px solid #ddd; border-radius: 5px; background: #f8f9fa; cursor: pointer;"> |
|
📝 Procédures d'inscription |
|
</button> |
|
<button onclick="document.querySelector('textarea').value='Comment trouver un logement étudiant au Maroc?'; document.querySelector('textarea').focus();" |
|
style="padding: 10px; border: 1px solid #ddd; border-radius: 5px; background: #f8f9fa; cursor: pointer;"> |
|
🏠 Logement étudiant |
|
</button> |
|
</div> |
|
</div> |
|
""") |
|
|
|
|
|
gr.HTML(""" |
|
<div class="footer-info"> |
|
<p><strong>Projet de fin d'études</strong> - Développé par [Votre nom]</p> |
|
<p>Ce chatbot utilise un modèle LLaMA 3.1 fine-tuné pour assister les étudiants marocains</p> |
|
<p><em>Pour toute question technique, contactez [[email protected]]</em></p> |
|
</div> |
|
""") |
|
|
|
|
|
def respond(message, history): |
|
return chatbot.generate_response(message, history) |
|
|
|
def clear_conversation(): |
|
return [], "" |
|
|
|
|
|
msg.submit(respond, [msg, chatbot_interface], [chatbot_interface, msg]) |
|
send_btn.click(respond, [msg, chatbot_interface], [chatbot_interface, msg]) |
|
clear_btn.click(clear_conversation, None, [chatbot_interface, msg]) |
|
|
|
return interface |
|
|
|
if __name__ == "__main__": |
|
|
|
interface = create_interface() |
|
interface.launch( |
|
server_name="0.0.0.0", |
|
server_port=7860, |
|
share=True, |
|
debug=True, |
|
show_error=True, |
|
inbrowser=True, |
|
) |
|
|