echarif commited on
Commit
63f176f
·
verified ·
1 Parent(s): 3f467fb

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +327 -0
  2. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from transformers import AutoTokenizer, AutoModelForCausalLM
3
+ from peft import PeftModel, PeftConfig
4
+ import torch
5
+ import re
6
+
7
+ class MoroccanStudentChatbot:
8
+ def __init__(self, adapter_model_id="echarif/llama_alpaca_lora_adapter"):
9
+ """Initialize the chatbot with the fine-tuned PEFT/LoRA model"""
10
+ print("Loading PEFT adapter config...")
11
+ self.config = PeftConfig.from_pretrained(adapter_model_id)
12
+
13
+ print("Loading base model...")
14
+ self.base_model = AutoModelForCausalLM.from_pretrained(
15
+ self.config.base_model_name_or_path,
16
+ return_dict=True,
17
+ device_map="auto",
18
+ torch_dtype=torch.float16
19
+ )
20
+
21
+ print("Loading LoRA adapter...")
22
+ self.model = PeftModel.from_pretrained(self.base_model, adapter_model_id)
23
+
24
+ print("Loading tokenizer...")
25
+ self.tokenizer = AutoTokenizer.from_pretrained(self.config.base_model_name_or_path)
26
+
27
+ # Add padding token if it doesn't exist
28
+ if self.tokenizer.pad_token is None:
29
+ self.tokenizer.pad_token = self.tokenizer.eos_token
30
+
31
+ 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.
32
+ ### Instruction :
33
+ {}
34
+ ### input :
35
+ {}
36
+ ### output :
37
+ {}"""
38
+
39
+ print("Model loaded successfully!")
40
+
41
+ def clean_output(self, raw_output):
42
+ """Clean the model output to remove unwanted tokens and formatting"""
43
+ # Decode the output
44
+ if isinstance(raw_output, torch.Tensor):
45
+ text = self.tokenizer.decode(raw_output[0], skip_special_tokens=True)
46
+ else:
47
+ text = raw_output
48
+
49
+ # Remove the prompt part and keep only the actual response
50
+ # Look for the "### output :" pattern and extract what comes after
51
+ output_pattern = r"### output :\s*(.*?)(?:<\|end_of_text\|>|$)"
52
+ match = re.search(output_pattern, text, re.DOTALL)
53
+
54
+ if match:
55
+ response = match.group(1).strip()
56
+ else:
57
+ # Fallback: try to extract text after "### output :"
58
+ if "### output :" in text:
59
+ response = text.split("### output :")[1].strip()
60
+ else:
61
+ response = text
62
+
63
+ # Clean up any remaining special tokens
64
+ response = re.sub(r'<\|.*?\|>', '', response)
65
+ response = re.sub(r'<.*?>', '', response)
66
+
67
+ # Remove extra whitespace and newlines
68
+ response = re.sub(r'\n+', '\n', response)
69
+ response = response.strip()
70
+
71
+ return response if response else "Je suis désolé, je n'ai pas pu générer une réponse appropriée."
72
+
73
+ def generate_response(self, user_input, history):
74
+ """Generate response for the chatbot"""
75
+ if not user_input.strip():
76
+ return history, ""
77
+
78
+ # Format the prompt
79
+ prompt = self.alpaca_prompt.format(user_input.strip(), "", "")
80
+
81
+ # Tokenize
82
+ inputs = self.tokenizer(prompt, return_tensors="pt", padding=True, truncation=True, max_length=512)
83
+ inputs = {k: v.to(self.model.device) for k, v in inputs.items()}
84
+
85
+ # Generate response
86
+ with torch.no_grad():
87
+ outputs = self.model.generate(
88
+ **inputs,
89
+ max_new_tokens=256,
90
+ do_sample=True,
91
+ temperature=0.7,
92
+ top_p=0.9,
93
+ pad_token_id=self.tokenizer.eos_token_id,
94
+ repetition_penalty=1.1,
95
+ )
96
+
97
+ # Clean the output
98
+ response = self.clean_output(outputs)
99
+
100
+ # Update history
101
+ history.append([user_input, response])
102
+
103
+ return history, ""
104
+
105
+ def create_interface():
106
+ """Create the Gradio interface"""
107
+
108
+ # Initialize the chatbot
109
+ chatbot = MoroccanStudentChatbot()
110
+
111
+ # Custom CSS for professional styling and responsiveness
112
+ custom_css = """
113
+ .header-container {
114
+ display: flex;
115
+ justify-content: space-between;
116
+ align-items: center;
117
+ padding: 20px;
118
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
119
+ border-radius: 10px;
120
+ margin-bottom: 20px;
121
+ color: white;
122
+ }
123
+
124
+ .logo-container {
125
+ display: flex;
126
+ align-items: center;
127
+ gap: 15px;
128
+ }
129
+
130
+ .logo-placeholder {
131
+ width: 60px;
132
+ height: 60px;
133
+ background: rgba(255, 255, 255, 0.2);
134
+ border-radius: 10px;
135
+ display: flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ font-size: 24px;
139
+ border: 2px solid rgba(255, 255, 255, 0.3);
140
+ }
141
+
142
+ .title-section h1 {
143
+ margin: 0;
144
+ font-size: 24px;
145
+ font-weight: bold;
146
+ }
147
+
148
+ .title-section p {
149
+ margin: 5px 0 0 0;
150
+ font-size: 14px;
151
+ opacity: 0.9;
152
+ }
153
+
154
+ .university-info {
155
+ text-align: right;
156
+ font-size: 12px;
157
+ opacity: 0.8;
158
+ }
159
+
160
+ /* Responsive design */
161
+ @media (max-width: 768px) {
162
+ .header-container {
163
+ flex-direction: column;
164
+ text-align: center;
165
+ gap: 15px;
166
+ }
167
+
168
+ .university-info {
169
+ text-align: center;
170
+ }
171
+
172
+ .title-section h1 {
173
+ font-size: 20px;
174
+ }
175
+
176
+ .logo-placeholder {
177
+ width: 50px;
178
+ height: 50px;
179
+ font-size: 20px;
180
+ }
181
+ }
182
+
183
+ .chatbot-container {
184
+ max-width: 800px;
185
+ margin: 0 auto;
186
+ }
187
+
188
+ .footer-info {
189
+ text-align: center;
190
+ margin-top: 20px;
191
+ padding: 15px;
192
+ background: #f8f9fa;
193
+ border-radius: 8px;
194
+ font-size: 12px;
195
+ color: #666;
196
+ }
197
+
198
+ /* Custom chatbot styling */
199
+ .gradio-container {
200
+ max-width: 1200px !important;
201
+ }
202
+
203
+ /* Improve mobile responsiveness */
204
+ @media (max-width: 480px) {
205
+ .header-container {
206
+ padding: 15px;
207
+ }
208
+
209
+ .title-section h1 {
210
+ font-size: 18px;
211
+ }
212
+
213
+ .logo-placeholder {
214
+ width: 40px;
215
+ height: 40px;
216
+ font-size: 16px;
217
+ }
218
+ }
219
+ """
220
+
221
+ # Create the interface
222
+ with gr.Blocks(css=custom_css, title="Assistant Étudiant Marocain", theme=gr.themes.Soft()) as interface:
223
+
224
+ # Header with logos and title
225
+ gr.HTML("""
226
+ <div class="header-container">
227
+ <div class="logo-container">
228
+ <div class="logo-placeholder">🎓</div>
229
+ <div class="title-section">
230
+ <h1>Assistant Étudiant Marocain</h1>
231
+ <p>Votre guide pour l'enseignement supérieur au Maroc</p>
232
+ </div>
233
+ </div>
234
+ <div class="university-info">
235
+ <div class="logo-placeholder">🏛️</div>
236
+ <div style="margin-top: 5px;">
237
+ <strong>Université [Nom de votre université]</strong><br>
238
+ Master [Votre spécialité]<br>
239
+ Projet de fin d'études
240
+ </div>
241
+ </div>
242
+ </div>
243
+ """)
244
+
245
+ with gr.Row():
246
+ with gr.Column(scale=1):
247
+ # Main chatbot interface
248
+ chatbot_interface = gr.Chatbot(
249
+ height=500,
250
+ 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.",
251
+ container=True,
252
+ bubble_full_width=False,
253
+ show_label=False,
254
+ elem_classes="chatbot-container"
255
+ )
256
+
257
+ with gr.Row():
258
+ msg = gr.Textbox(
259
+ placeholder="Tapez votre question ici...",
260
+ container=False,
261
+ scale=7,
262
+ min_width=0,
263
+ )
264
+ send_btn = gr.Button("Envoyer", variant="primary", scale=1, min_width=0)
265
+
266
+ # Clear button
267
+ clear_btn = gr.Button("Nouvelle conversation", variant="secondary", size="sm")
268
+
269
+ # Example questions
270
+ gr.HTML("""
271
+ <div style="margin-top: 20px;">
272
+ <h3>💡 Questions d'exemple :</h3>
273
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 10px; margin-top: 10px;">
274
+ <button onclick="document.querySelector('textarea').value='Quelles sont les meilleures universités pour étudier l\\'informatique au Maroc?'; document.querySelector('textarea').focus();"
275
+ style="padding: 10px; border: 1px solid #ddd; border-radius: 5px; background: #f8f9fa; cursor: pointer;">
276
+ 🏫 Universités d'informatique
277
+ </button>
278
+ <button onclick="document.querySelector('textarea').value='Comment obtenir une bourse d\\'études au Maroc?'; document.querySelector('textarea').focus();"
279
+ style="padding: 10px; border: 1px solid #ddd; border-radius: 5px; background: #f8f9fa; cursor: pointer;">
280
+ 💰 Bourses d'études
281
+ </button>
282
+ <button onclick="document.querySelector('textarea').value='Quelles sont les procédures d\\'inscription dans les universités publiques?'; document.querySelector('textarea').focus();"
283
+ style="padding: 10px; border: 1px solid #ddd; border-radius: 5px; background: #f8f9fa; cursor: pointer;">
284
+ 📝 Procédures d'inscription
285
+ </button>
286
+ <button onclick="document.querySelector('textarea').value='Comment trouver un logement étudiant au Maroc?'; document.querySelector('textarea').focus();"
287
+ style="padding: 10px; border: 1px solid #ddd; border-radius: 5px; background: #f8f9fa; cursor: pointer;">
288
+ 🏠 Logement étudiant
289
+ </button>
290
+ </div>
291
+ </div>
292
+ """)
293
+
294
+ # Footer
295
+ gr.HTML("""
296
+ <div class="footer-info">
297
+ <p><strong>Projet de fin d'études</strong> - Développé par [Votre nom]</p>
298
+ <p>Ce chatbot utilise un modèle LLaMA 3.1 fine-tuné pour assister les étudiants marocains</p>
299
+ <p><em>Pour toute question technique, contactez [[email protected]]</em></p>
300
+ </div>
301
+ """)
302
+
303
+ # Event handlers
304
+ def respond(message, history):
305
+ return chatbot.generate_response(message, history)
306
+
307
+ def clear_conversation():
308
+ return [], ""
309
+
310
+ # Connect the events
311
+ msg.submit(respond, [msg, chatbot_interface], [chatbot_interface, msg])
312
+ send_btn.click(respond, [msg, chatbot_interface], [chatbot_interface, msg])
313
+ clear_btn.click(clear_conversation, None, [chatbot_interface, msg])
314
+
315
+ return interface
316
+
317
+ if __name__ == "__main__":
318
+ # Launch the interface
319
+ interface = create_interface()
320
+ interface.launch(
321
+ server_name="0.0.0.0", # Makes it accessible from other devices on the network
322
+ server_port=7860,
323
+ share=True, # Creates a public link
324
+ debug=True,
325
+ show_error=True,
326
+ inbrowser=True, # Opens automatically in browser
327
+ )
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ torch
2
+ transformers
3
+ bitsandbytes
4
+ peft
5
+ gradio