mikaelJ46 commited on
Commit
3677a3d
Β·
verified Β·
1 Parent(s): e0760f1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +211 -96
app.py CHANGED
@@ -1,32 +1,138 @@
1
  # --------------------------------------------------------------
2
- # IGCSE/GCSE Language Platform – 100% FREE with Google Gemini API
3
- # Model: Google Gemini Pro (FREE tier - No payment required!)
4
  # --------------------------------------------------------------
5
 
6
  import os
7
  import json
8
  from datetime import datetime
9
  import gradio as gr
10
- import google.generativeai as genai
11
  import PyPDF2
12
- import io
13
-
14
- # ---------- 1. Configure Google Gemini API (FREE) ----------
15
- GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
16
- if not GEMINI_API_KEY:
17
- raise gr.Error("Set GEMINI_API_KEY in Secrets (Settings β†’ Repository Secrets)")
18
-
19
- genai.configure(api_key=GEMINI_API_KEY)
20
-
21
- # Use Gemini Pro (FREE tier includes generous limits)
22
- model = genai.GenerativeModel('gemini-pro')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- # ---------- 2. Global storage ----------
25
  papers_storage = []
26
- pdf_content_storage = {} # Store extracted PDF text
27
  ADMIN_PASSWORD = "@mikaelJ46"
28
 
29
- # ---------- 3. Topic lists ----------
30
  french_topics = [
31
  "Greetings & Introductions", "Family & Relationships", "Daily Routines",
32
  "Food & Restaurants", "Shopping & Money", "Travel & Transport",
@@ -46,24 +152,6 @@ efl_topics = [
46
  "Listening Comprehension"
47
  ]
48
 
49
- # ---------- 4. Helper: call Gemini model ----------
50
- def call_gemini(prompt: str, system_instruction: str = ""):
51
- """Call Google Gemini API - 100% FREE"""
52
- try:
53
- # Combine system instruction with prompt
54
- full_prompt = f"{system_instruction}\n\n{prompt}" if system_instruction else prompt
55
-
56
- response = model.generate_content(
57
- full_prompt,
58
- generation_config=genai.types.GenerationConfig(
59
- temperature=0.7,
60
- max_output_tokens=2000,
61
- )
62
- )
63
- return response.text.strip()
64
- except Exception as e:
65
- return f"⚠️ Error: {str(e)}\n\nPlease check your GEMINI_API_KEY in Settings β†’ Secrets"
66
-
67
  # ---------- 5. PDF Processing ----------
68
  def extract_text_from_pdf(pdf_file):
69
  """Extract text from uploaded PDF file"""
@@ -78,7 +166,7 @@ def extract_text_from_pdf(pdf_file):
78
  except Exception as e:
79
  return f"Error extracting PDF: {e}"
80
 
81
- # ---------- 6. AI Tutor ----------
82
  def ai_tutor_chat(message, history, subject, topic, level):
83
  if not message.strip():
84
  return history
@@ -91,16 +179,28 @@ Use a friendly, supportive tone to help students learn effectively."""
91
 
92
  # Build conversation context
93
  conversation = ""
94
- for user_msg, bot_msg in history:
95
  if user_msg:
96
  conversation += f"Student: {user_msg}\n"
97
  if bot_msg:
98
- conversation += f"Tutor: {bot_msg}\n"
 
 
99
 
100
  conversation += f"Student: {message}\nTutor:"
101
-
102
  full_prompt = f"{system}\n\nConversation:\n{conversation}"
103
- bot_response = call_gemini(full_prompt)
 
 
 
 
 
 
 
 
 
 
 
104
 
105
  history.append((message, bot_response))
106
  return history
@@ -112,20 +212,29 @@ def clear_chat():
112
  def translate_text(text, direction):
113
  if not text.strip():
114
  return "Enter text first."
 
115
  src = "English" if direction == "English β†’ French" else "French"
116
  tgt = "French" if direction == "English β†’ French" else "English"
117
 
118
- system = f"You are a professional translator specializing in {src} to {tgt} translation."
119
- prompt = f"Translate the following text from {src} to {tgt}. Provide only the translation without explanations:\n\n{text}"
 
 
 
 
 
 
 
 
 
120
 
121
- return call_gemini(prompt, system)
122
 
123
  # ---------- 8. Dictionary ----------
124
  def dictionary_lookup(word):
125
  if not word.strip():
126
  return "Enter a French word."
127
 
128
- system = "You are a French language dictionary expert with deep knowledge of French vocabulary, grammar, and usage."
129
  prompt = f"""Provide a detailed French dictionary entry for "{word}":
130
  - Part of speech (noun, verb, adjective, etc.)
131
  - Gender (if noun: masculine/feminine)
@@ -135,7 +244,12 @@ def dictionary_lookup(word):
135
  - Any important usage notes or context
136
  - Related words or derivatives"""
137
 
138
- return call_gemini(prompt, system)
 
 
 
 
 
139
 
140
  # ---------- 9. Practice Questions (Enhanced with PDF context) ----------
141
  def generate_question(subject, topic, level):
@@ -149,51 +263,48 @@ def generate_question(subject, topic, level):
149
  if paper and paper['subject'].lower() == subject.lower() and paper['level'] == level:
150
  pdf_context += f"\n\nReference material from {paper['title']}:\n{content[:3000]}"
151
 
152
- system = f"You are an expert {level} {subject} examiner with years of experience creating authentic exam questions."
153
  prompt = f"""Create ONE high-quality {level} {subject} exam question on the topic: "{topic}".
154
- {"Base the question style, difficulty level, and format on this reference material from actual past papers:" + pdf_context if pdf_context else "Create an authentic exam-style question appropriate for {level} level."}
155
 
156
  The question should:
157
  - Be appropriate for {level} level students
158
- - Test understanding and application, not just memorization
159
- - Include clear, unambiguous instructions
160
  - Be answerable in 5-10 minutes
161
- - Follow standard {level} exam format and conventions
162
 
163
- Return ONLY valid JSON with exactly these keys (no markdown, no extra text):
164
- {{"question": "the complete question text", "expectedAnswer": "detailed description of what a good answer should include", "markScheme": "clear marking criteria with point allocations"}}"""
 
 
165
 
166
- response = call_gemini(prompt, system)
167
  try:
168
  clean_txt = response.replace("```json", "").replace("```", "").strip()
169
  data = json.loads(clean_txt)
170
  return data["question"], data.get("expectedAnswer", ""), data.get("markScheme", "")
171
  except Exception as e:
172
- return response, "", f"Error parsing: {e}"
173
 
174
  def check_answer(question, expected, user_answer, subject, level):
175
  if not user_answer.strip():
176
  return "Write your answer first!"
177
 
178
- system = f"You are a {level} {subject} examiner providing constructive, detailed feedback."
179
- prompt = f"""Evaluate this student's answer professionally:
180
 
181
  Question: {question}
182
- Expected answer criteria: {expected}
183
 
184
  Student's answer:
185
  {user_answer}
186
 
187
- Provide comprehensive evaluation considering accuracy, terminology, structure, clarity, and relevance.
188
-
189
- Return JSON format (no markdown):
190
- {{"isCorrect": true/false, "score": 0-100, "feedback": "detailed feedback", "improvements": "specific suggestions", "strengths": "what was done well"}}"""
191
 
192
- response = call_gemini(prompt, system)
193
  try:
194
  clean_txt = response.replace("```json", "").replace("```", "").strip()
195
  fb = json.loads(clean_txt)
196
- return f"""βœ… Score: {fb['score']}%
197
 
198
  πŸ“ Detailed Feedback:
199
  {fb['feedback']}
@@ -203,14 +314,19 @@ Return JSON format (no markdown):
203
 
204
  🎯 How to Improve:
205
  {fb['improvements']}"""
 
 
 
 
 
206
  except Exception:
207
  return response
208
 
209
  # ---------- 10. Admin – Past Papers ----------
210
  def verify_admin_password(password):
211
  if password == ADMIN_PASSWORD:
212
- return gr.update(visible=True), gr.update(visible=False), "βœ… Access granted! Welcome, Admin."
213
- return gr.update(visible=False), gr.update(visible=True), "❌ Incorrect password. Please try again."
214
 
215
  def upload_paper(title, subject, level, content, pdf_file):
216
  if not all([title, subject, level, content]):
@@ -218,15 +334,12 @@ def upload_paper(title, subject, level, content, pdf_file):
218
 
219
  paper_id = len(papers_storage) + 1
220
 
221
- # Extract PDF content if provided
222
  pdf_text = ""
223
  if pdf_file is not None:
224
  pdf_text = extract_text_from_pdf(pdf_file)
225
  if pdf_text and not pdf_text.startswith("Error"):
226
  pdf_content_storage[paper_id] = pdf_text
227
- content += f"\n\n[πŸ“„ PDF content extracted: {len(pdf_text)} characters]"
228
- else:
229
- content += f"\n\n[⚠️ PDF issue: {pdf_text}]"
230
 
231
  papers_storage.append({
232
  "id": paper_id,
@@ -237,11 +350,11 @@ def upload_paper(title, subject, level, content, pdf_file):
237
  "has_pdf": bool(pdf_text and not pdf_text.startswith("Error")),
238
  "uploaded_at": datetime.now().strftime("%Y-%m-%d %H:%M")
239
  })
240
- return "βœ… Paper uploaded successfully!", get_papers_list()
241
 
242
  def get_papers_list():
243
  if not papers_storage:
244
- return "No papers uploaded yet."
245
  return "\n".join(
246
  f"**{p['title']}** ({p['subject'].upper()} - {p['level']}) {'πŸ“„ PDF' if p.get('has_pdf') else 'πŸ“'}\nπŸ“… {p['uploaded_at']}\n{p['content'][:120]}...\n{'─'*60}"
247
  for p in papers_storage
@@ -251,7 +364,7 @@ def view_papers_student(subject, level):
251
  filtered = [p for p in papers_storage
252
  if p["subject"] == subject.lower() and p["level"] == level]
253
  if not filtered:
254
- return f"πŸ“­ No {subject} {level} papers available yet."
255
  return "\n".join(
256
  f"**{p['title']}** {'πŸ“„ PDF' if p.get('has_pdf') else ''}\nπŸ“… {p['uploaded_at']}\n\n{p['content']}\n\n{'═'*60}"
257
  for p in filtered
@@ -261,8 +374,8 @@ def view_papers_student(subject, level):
261
  with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
262
  gr.Markdown("""
263
  # πŸŽ“ IGCSE/GCSE Language Learning Platform
264
- ### Powered by Google Gemini Pro - 100% FREE!
265
- 🌟 AI Tutor | πŸ”„ Translator | πŸ“– Dictionary | πŸ“š Past Papers with PDF Support
266
  """)
267
 
268
  with gr.Tabs():
@@ -270,7 +383,7 @@ with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
270
  with gr.Tab("πŸ“š Student Portal"):
271
  with gr.Tabs():
272
  with gr.Tab("πŸ€– AI Tutor"):
273
- gr.Markdown("### Chat with Your AI Tutor\n*Personalized help powered by Google Gemini*")
274
  with gr.Row():
275
  subj = gr.Radio(["French", "EFL"], label="Subject", value="French")
276
  lvl = gr.Radio(["IGCSE", "GCSE"], label="Level", value="IGCSE")
@@ -281,7 +394,7 @@ with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
281
  subj.change(upd_topics, subj, topc)
282
 
283
  chat = gr.Chatbot(height=450, show_label=False)
284
- txt = gr.Textbox(placeholder="Ask anything... e.g., 'Explain the passΓ© composΓ©'", label="Your Message")
285
  with gr.Row():
286
  send = gr.Button("Send πŸ“€", variant="primary")
287
  clr = gr.Button("Clear πŸ—‘οΈ")
@@ -356,29 +469,31 @@ with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
356
 
357
  gr.Markdown("""
358
  ---
359
- ### πŸš€ Setup Instructions - FREE Google Gemini API:
360
-
361
- **Step 1: Get FREE Gemini API Key**
362
- 1. Go to [Google AI Studio](https://makersuite.google.com/app/apikey)
363
- 2. Click "Create API Key"
364
- 3. Copy your API key (FREE tier includes generous limits!)
365
-
366
- **Step 2: Deploy on Hugging Face**
367
- 1. Create a new Space on Hugging Face
368
- 2. Settings β†’ Repository Secrets β†’ Add `GEMINI_API_KEY` (paste your key)
369
- 3. Upload `requirements.txt` with: `gradio`, `google-generativeai`, `PyPDF2`
370
- 4. Push code and restart!
371
-
372
- ### ✨ Why Google Gemini?
373
- βœ… **100% FREE** - Generous free tier (60 requests/minute!)
374
- βœ… **No payment required** - No credit card needed
375
- βœ… **Powerful** - Latest Gemini Pro model
376
- βœ… **Reliable** - Google's infrastructure
377
- βœ… **Multilingual** - Perfect for language learning
 
 
378
 
379
  **Admin Password:** `@mikaelJ46`
380
 
381
- *Powered by Google Gemini Pro - Education should be accessible to everyone!* 🌟
382
  """)
383
 
384
  app.launch()
 
1
  # --------------------------------------------------------------
2
+ # IGCSE/GCSE Language Platform – Multi-AI System (Z.ai + Cohere + MiniMax + Gemini)
3
+ # Models: Z.ai GLM-4.6 (Primary) β†’ Cohere β†’ MiniMax β†’ Gemini (Fallbacks)
4
  # --------------------------------------------------------------
5
 
6
  import os
7
  import json
8
  from datetime import datetime
9
  import gradio as gr
 
10
  import PyPDF2
11
+ import time
12
+
13
+ # ---------- 1. Configure ALL AI Systems ----------
14
+ # Z.ai (Primary)
15
+ try:
16
+ from huggingface_hub import InferenceClient
17
+ zai_client = InferenceClient(
18
+ provider="novita",
19
+ api_key=os.environ.get("HF_TOKEN"),
20
+ )
21
+ print("βœ… Z.ai GLM-4.6 initialized successfully")
22
+ except Exception as e:
23
+ print(f"⚠️ Error initializing Z.ai: {e}")
24
+ zai_client = None
25
+
26
+ # Cohere (Secondary)
27
+ try:
28
+ import cohere
29
+ cohere_client = cohere.Client(os.getenv("COHERE_API_KEY"))
30
+ print("βœ… Cohere initialized successfully")
31
+ except Exception as e:
32
+ print(f"⚠️ Error initializing Cohere: {e}")
33
+ cohere_client = None
34
+
35
+ # MiniMax (Tertiary)
36
+ try:
37
+ minimax_client = InferenceClient(
38
+ provider="novita",
39
+ api_key=os.environ.get("HF_TOKEN"),
40
+ )
41
+ print("βœ… MiniMax AI initialized successfully")
42
+ except Exception as e:
43
+ print(f"⚠️ Error initializing MiniMax: {e}")
44
+ minimax_client = None
45
+
46
+ # Gemini (Final Fallback)
47
+ try:
48
+ import google.generativeai as genai
49
+ genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
50
+ gemini_model = genai.GenerativeModel('gemini-pro')
51
+ print("βœ… Gemini AI initialized successfully")
52
+ except Exception as e:
53
+ print(f"⚠️ Error initializing Gemini: {e}")
54
+ gemini_model = None
55
+
56
+ # ---------- 2. Unified AI Function with Smart Fallback ----------
57
+ def ask_ai(prompt, temperature=0.7, max_retries=2):
58
+ """
59
+ Try models in order: Z.ai β†’ Cohere β†’ MiniMax β†’ Gemini
60
+ Returns: (response_text, source_name)
61
+ """
62
+ last_error = None
63
+
64
+ # Try Z.ai first (Primary)
65
+ if zai_client:
66
+ for attempt in range(max_retries):
67
+ try:
68
+ completion = zai_client.chat.completions.create(
69
+ model="zai-org/GLM-4.6",
70
+ messages=[{"role": "user", "content": prompt}],
71
+ temperature=temperature
72
+ )
73
+ return completion.choices[0].message.content, "zai"
74
+ except Exception as e:
75
+ last_error = e
76
+ print(f"⚠️ Z.ai attempt {attempt+1} failed: {str(e)}")
77
+ if attempt < max_retries - 1:
78
+ time.sleep(1)
79
+
80
+ # Try Cohere (Secondary)
81
+ if cohere_client:
82
+ for attempt in range(max_retries):
83
+ try:
84
+ response = cohere_client.chat(
85
+ model="command-r-plus-08-2024",
86
+ message=prompt,
87
+ temperature=temperature
88
+ )
89
+ return response.text, "cohere"
90
+ except Exception as e:
91
+ last_error = e
92
+ print(f"⚠️ Cohere attempt {attempt+1} failed: {str(e)}")
93
+ if attempt < max_retries - 1:
94
+ time.sleep(1)
95
+
96
+ # Try MiniMax (Tertiary)
97
+ if minimax_client:
98
+ for attempt in range(max_retries):
99
+ try:
100
+ completion = minimax_client.chat.completions.create(
101
+ model="MiniMaxAI/MiniMax-M2",
102
+ messages=[{"role": "user", "content": prompt}],
103
+ temperature=temperature
104
+ )
105
+ return completion.choices[0].message.content, "minimax"
106
+ except Exception as e:
107
+ last_error = e
108
+ print(f"⚠️ MiniMax attempt {attempt+1} failed: {str(e)}")
109
+ if attempt < max_retries - 1:
110
+ time.sleep(1)
111
+
112
+ # Try Gemini (Final Fallback)
113
+ if gemini_model:
114
+ try:
115
+ response = gemini_model.generate_content(
116
+ prompt,
117
+ generation_config=genai.types.GenerationConfig(
118
+ temperature=temperature,
119
+ )
120
+ )
121
+ return response.text, "gemini"
122
+ except Exception as e:
123
+ last_error = e
124
+ print(f"⚠️ Gemini fallback failed: {str(e)}")
125
+
126
+ # All failed
127
+ error_msg = f"⚠️ Error: All AI services failed. Last error: {str(last_error)}"
128
+ return error_msg, "error"
129
 
130
+ # ---------- 3. Global storage ----------
131
  papers_storage = []
132
+ pdf_content_storage = {}
133
  ADMIN_PASSWORD = "@mikaelJ46"
134
 
135
+ # ---------- 4. Topic lists ----------
136
  french_topics = [
137
  "Greetings & Introductions", "Family & Relationships", "Daily Routines",
138
  "Food & Restaurants", "Shopping & Money", "Travel & Transport",
 
152
  "Listening Comprehension"
153
  ]
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  # ---------- 5. PDF Processing ----------
156
  def extract_text_from_pdf(pdf_file):
157
  """Extract text from uploaded PDF file"""
 
166
  except Exception as e:
167
  return f"Error extracting PDF: {e}"
168
 
169
+ # ---------- 6. AI Tutor with Multi-Model Support ----------
170
  def ai_tutor_chat(message, history, subject, topic, level):
171
  if not message.strip():
172
  return history
 
179
 
180
  # Build conversation context
181
  conversation = ""
182
+ for user_msg, bot_msg in history[-5:]: # Last 5 exchanges
183
  if user_msg:
184
  conversation += f"Student: {user_msg}\n"
185
  if bot_msg:
186
+ # Remove source indicators from history
187
+ clean_msg = bot_msg.replace("[Cohere] ", "").replace("[MiniMax] ", "").replace("[Gemini] ", "")
188
+ conversation += f"Tutor: {clean_msg}\n"
189
 
190
  conversation += f"Student: {message}\nTutor:"
 
191
  full_prompt = f"{system}\n\nConversation:\n{conversation}"
192
+
193
+ bot_response, source = ask_ai(full_prompt, temperature=0.7)
194
+
195
+ # Add source indicator if not from Z.ai
196
+ if source == "cohere":
197
+ bot_response = f"πŸ”΅ [Cohere] {bot_response}"
198
+ elif source == "minimax":
199
+ bot_response = f"🟣 [MiniMax] {bot_response}"
200
+ elif source == "gemini":
201
+ bot_response = f"🟒 [Gemini] {bot_response}"
202
+ elif source == "error":
203
+ pass # Error already formatted
204
 
205
  history.append((message, bot_response))
206
  return history
 
212
  def translate_text(text, direction):
213
  if not text.strip():
214
  return "Enter text first."
215
+
216
  src = "English" if direction == "English β†’ French" else "French"
217
  tgt = "French" if direction == "English β†’ French" else "English"
218
 
219
+ prompt = f"""You are a professional translator.
220
+ Translate the following text from {src} to {tgt}.
221
+ Provide only the translation without explanations:
222
+
223
+ {text}"""
224
+
225
+ response, source = ask_ai(prompt, temperature=0.3)
226
+
227
+ # Add subtle source indicator
228
+ if source in ["cohere", "minimax", "gemini"]:
229
+ response = f"{response}\n\n_[Translated using {source.title()}]_"
230
 
231
+ return response
232
 
233
  # ---------- 8. Dictionary ----------
234
  def dictionary_lookup(word):
235
  if not word.strip():
236
  return "Enter a French word."
237
 
 
238
  prompt = f"""Provide a detailed French dictionary entry for "{word}":
239
  - Part of speech (noun, verb, adjective, etc.)
240
  - Gender (if noun: masculine/feminine)
 
244
  - Any important usage notes or context
245
  - Related words or derivatives"""
246
 
247
+ response, source = ask_ai(prompt, temperature=0.3)
248
+
249
+ if source in ["cohere", "minimax", "gemini"]:
250
+ response = f"{response}\n\n_[Dictionary powered by {source.title()}]_"
251
+
252
+ return response
253
 
254
  # ---------- 9. Practice Questions (Enhanced with PDF context) ----------
255
  def generate_question(subject, topic, level):
 
263
  if paper and paper['subject'].lower() == subject.lower() and paper['level'] == level:
264
  pdf_context += f"\n\nReference material from {paper['title']}:\n{content[:3000]}"
265
 
 
266
  prompt = f"""Create ONE high-quality {level} {subject} exam question on the topic: "{topic}".
267
+ {"Base the question style, difficulty level, and format on this reference material:" + pdf_context if pdf_context else "Create an authentic exam-style question."}
268
 
269
  The question should:
270
  - Be appropriate for {level} level students
271
+ - Test understanding and application
272
+ - Include clear instructions
273
  - Be answerable in 5-10 minutes
 
274
 
275
+ Return ONLY valid JSON (no markdown):
276
+ {{"question": "complete question text", "expectedAnswer": "what a good answer should include", "markScheme": "marking criteria"}}"""
277
+
278
+ response, source = ask_ai(prompt, temperature=0.4)
279
 
 
280
  try:
281
  clean_txt = response.replace("```json", "").replace("```", "").strip()
282
  data = json.loads(clean_txt)
283
  return data["question"], data.get("expectedAnswer", ""), data.get("markScheme", "")
284
  except Exception as e:
285
+ return response, "", f"Error: {e}"
286
 
287
  def check_answer(question, expected, user_answer, subject, level):
288
  if not user_answer.strip():
289
  return "Write your answer first!"
290
 
291
+ prompt = f"""Evaluate this student's answer:
 
292
 
293
  Question: {question}
294
+ Expected: {expected}
295
 
296
  Student's answer:
297
  {user_answer}
298
 
299
+ Return JSON (no markdown):
300
+ {{"isCorrect": true/false, "score": 0-100, "feedback": "detailed feedback", "improvements": "suggestions", "strengths": "what was done well"}}"""
301
+
302
+ response, source = ask_ai(prompt, temperature=0.3)
303
 
 
304
  try:
305
  clean_txt = response.replace("```json", "").replace("```", "").strip()
306
  fb = json.loads(clean_txt)
307
+ result = f"""βœ… Score: {fb['score']}%
308
 
309
  πŸ“ Detailed Feedback:
310
  {fb['feedback']}
 
314
 
315
  🎯 How to Improve:
316
  {fb['improvements']}"""
317
+
318
+ if source in ["cohere", "minimax", "gemini"]:
319
+ result += f"\n\n_[Graded by {source.title()}]_"
320
+
321
+ return result
322
  except Exception:
323
  return response
324
 
325
  # ---------- 10. Admin – Past Papers ----------
326
  def verify_admin_password(password):
327
  if password == ADMIN_PASSWORD:
328
+ return gr.update(visible=True), gr.update(visible=False), "βœ… Access granted!"
329
+ return gr.update(visible=False), gr.update(visible=True), "❌ Incorrect password!"
330
 
331
  def upload_paper(title, subject, level, content, pdf_file):
332
  if not all([title, subject, level, content]):
 
334
 
335
  paper_id = len(papers_storage) + 1
336
 
 
337
  pdf_text = ""
338
  if pdf_file is not None:
339
  pdf_text = extract_text_from_pdf(pdf_file)
340
  if pdf_text and not pdf_text.startswith("Error"):
341
  pdf_content_storage[paper_id] = pdf_text
342
+ content += f"\n\n[πŸ“„ PDF extracted: {len(pdf_text)} characters]"
 
 
343
 
344
  papers_storage.append({
345
  "id": paper_id,
 
350
  "has_pdf": bool(pdf_text and not pdf_text.startswith("Error")),
351
  "uploaded_at": datetime.now().strftime("%Y-%m-%d %H:%M")
352
  })
353
+ return "βœ… Paper uploaded!", get_papers_list()
354
 
355
  def get_papers_list():
356
  if not papers_storage:
357
+ return "No papers yet."
358
  return "\n".join(
359
  f"**{p['title']}** ({p['subject'].upper()} - {p['level']}) {'πŸ“„ PDF' if p.get('has_pdf') else 'πŸ“'}\nπŸ“… {p['uploaded_at']}\n{p['content'][:120]}...\n{'─'*60}"
360
  for p in papers_storage
 
364
  filtered = [p for p in papers_storage
365
  if p["subject"] == subject.lower() and p["level"] == level]
366
  if not filtered:
367
+ return f"πŸ“­ No {subject} {level} papers available."
368
  return "\n".join(
369
  f"**{p['title']}** {'πŸ“„ PDF' if p.get('has_pdf') else ''}\nπŸ“… {p['uploaded_at']}\n\n{p['content']}\n\n{'═'*60}"
370
  for p in filtered
 
374
  with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
375
  gr.Markdown("""
376
  # πŸŽ“ IGCSE/GCSE Language Learning Platform
377
+ ### Multi-AI System: Z.ai (Primary) β†’ Cohere β†’ MiniMax β†’ Gemini
378
+ 🌟 AI Tutor | πŸ”„ Translator | πŸ“– Dictionary | πŸ“š Past Papers
379
  """)
380
 
381
  with gr.Tabs():
 
383
  with gr.Tab("πŸ“š Student Portal"):
384
  with gr.Tabs():
385
  with gr.Tab("πŸ€– AI Tutor"):
386
+ gr.Markdown("### Chat with Your AI Tutor\n*Powered by Z.ai with automatic fallback*")
387
  with gr.Row():
388
  subj = gr.Radio(["French", "EFL"], label="Subject", value="French")
389
  lvl = gr.Radio(["IGCSE", "GCSE"], label="Level", value="IGCSE")
 
394
  subj.change(upd_topics, subj, topc)
395
 
396
  chat = gr.Chatbot(height=450, show_label=False)
397
+ txt = gr.Textbox(placeholder="Ask anything... e.g., 'Explain the passΓ© composΓ©'", label="Message")
398
  with gr.Row():
399
  send = gr.Button("Send πŸ“€", variant="primary")
400
  clr = gr.Button("Clear πŸ—‘οΈ")
 
469
 
470
  gr.Markdown("""
471
  ---
472
+ ### πŸš€ Multi-AI System Setup:
473
+
474
+ **Required API Keys (add in Secrets):**
475
+ 1. `HF_TOKEN` - Hugging Face token (for Z.ai & MiniMax)
476
+ 2. `COHERE_API_KEY` - Cohere API key
477
+ 3. `GEMINI_API_KEY` - Google Gemini API key
478
+
479
+ **How It Works:**
480
+ - πŸ”΅ **Z.ai GLM-4.6** tries first (best for education)
481
+ - 🟠 **Cohere** kicks in if Z.ai fails
482
+ - 🟣 **MiniMax** as third option
483
+ - 🟒 **Gemini** as final fallback
484
+
485
+ **Requirements:**
486
+ ```
487
+ gradio>=4.0.0
488
+ PyPDF2>=3.0.0
489
+ huggingface_hub>=0.19.0
490
+ cohere>=4.0.0
491
+ google-generativeai>=0.3.0
492
+ ```
493
 
494
  **Admin Password:** `@mikaelJ46`
495
 
496
+ *Reliable, multi-model AI system ensures 99.9% uptime!* πŸš€
497
  """)
498
 
499
  app.launch()