LiamKhoaLe commited on
Commit
d9bd342
·
1 Parent(s): b911d8c

Update timetable creator

Browse files
Files changed (1) hide show
  1. app.py +88 -7
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # Access site: https://binkhoale1812-tutorbot.hf.space/chat
2
  import os
3
  import time
4
  import uvicorn
@@ -13,13 +13,13 @@ from fastapi.middleware.cors import CORSMiddleware
13
  from google import genai
14
  from gradio_client import Client, handle_file
15
 
16
- # —————— Logging ——————
17
  logging.basicConfig(level=logging.DEBUG, format="%(asctime)s — %(name)s — %(levelname)s — %(message)s", force=True)
18
  logger = logging.getLogger("tutor-chatbot")
19
  logger.setLevel(logging.DEBUG)
20
  logger.info("🚀 Starting Tutor Chatbot API...")
21
 
22
- # —————— Environment ——————
23
  gemini_flash_api_key = os.getenv("FlashAPI")
24
  if not gemini_flash_api_key:
25
  raise ValueError("❌ Missing Gemini Flash API key!")
@@ -35,7 +35,7 @@ check_system_resources()
35
  os.environ["OMP_NUM_THREADS"] = "1"
36
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
37
 
38
- # —————— FastAPI Setup ——————
39
  app = FastAPI(title="Tutor Chatbot API")
40
  app.add_middleware(
41
  CORSMiddleware,
@@ -59,7 +59,7 @@ def gemini_flash_completion(prompt, model="gemini-2.5-flash-preview-04-17", temp
59
  logger.error(f"❌ Gemini error: {e}")
60
  return "Error generating response from Gemini."
61
 
62
- # —————— Qwen 2.5 VL Client Setup ——————
63
  qwen_client = Client("prithivMLmods/Qwen2.5-VL-7B-Instruct")
64
  logger.info("[Qwen] Using remote API via Gradio Client")
65
 
@@ -120,7 +120,7 @@ def qwen_image_summary(image_file: UploadFile, subject: str, level: str) -> str:
120
  raise HTTPException(500, "❌ Qwen image analysis failed")
121
 
122
 
123
- # —————— Unified Chat Endpoint ——————
124
  @app.post("/chat")
125
  async def chat_endpoint(
126
  query: str = Form(""),
@@ -205,7 +205,88 @@ async def chat_endpoint(
205
  response_text += f"\n\n*(Response time: {end_time - start_time:.2f} seconds)*"
206
  return JSONResponse(content={"response": response_text})
207
 
208
- # —————— Launch Server ——————
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  if __name__ == "__main__":
210
  logger.info("✅ Launching FastAPI server...")
211
  try:
 
1
+ # Access site: https://binkhoale1812-tutorbot.hf.space
2
  import os
3
  import time
4
  import uvicorn
 
13
  from google import genai
14
  from gradio_client import Client, handle_file
15
 
16
+ # ———————— Logging —————————
17
  logging.basicConfig(level=logging.DEBUG, format="%(asctime)s — %(name)s — %(levelname)s — %(message)s", force=True)
18
  logger = logging.getLogger("tutor-chatbot")
19
  logger.setLevel(logging.DEBUG)
20
  logger.info("🚀 Starting Tutor Chatbot API...")
21
 
22
+ # —————— Environment ———————
23
  gemini_flash_api_key = os.getenv("FlashAPI")
24
  if not gemini_flash_api_key:
25
  raise ValueError("❌ Missing Gemini Flash API key!")
 
35
  os.environ["OMP_NUM_THREADS"] = "1"
36
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
37
 
38
+ # —————— FastAPI Setup —————
39
  app = FastAPI(title="Tutor Chatbot API")
40
  app.add_middleware(
41
  CORSMiddleware,
 
59
  logger.error(f"❌ Gemini error: {e}")
60
  return "Error generating response from Gemini."
61
 
62
+ # —— Qwen 2.5 VL Client Setup —————
63
  qwen_client = Client("prithivMLmods/Qwen2.5-VL-7B-Instruct")
64
  logger.info("[Qwen] Using remote API via Gradio Client")
65
 
 
120
  raise HTTPException(500, "❌ Qwen image analysis failed")
121
 
122
 
123
+ # ————— Unified Chat Endpoint —————
124
  @app.post("/chat")
125
  async def chat_endpoint(
126
  query: str = Form(""),
 
205
  response_text += f"\n\n*(Response time: {end_time - start_time:.2f} seconds)*"
206
  return JSONResponse(content={"response": response_text})
207
 
208
+
209
+ # ————— Clsr Pydantic Schema —————
210
+ from pydantic import BaseModel, Field, validator
211
+ from typing import Optional, List, Literal
212
+ # Dynamic cls
213
+ class StudyPreferences(BaseModel):
214
+ daysPerWeek: int = Field(..., ge=1, le=7)
215
+ hoursPerSession: float = Field(..., ge=0.5, le=4)
216
+ learningStyle: Literal["step-by-step", "conceptual", "visual"]
217
+ # Dynamic cls
218
+ class ClassroomRequest(BaseModel):
219
+ id: str
220
+ name: str = Field(..., min_length=2)
221
+ role: Literal["tutor", "student"]
222
+ subject: str
223
+ gradeLevel: str
224
+ textbookUrl: Optional[str] = None
225
+ syllabusUrl: Optional[str] = None
226
+ studyPreferences: StudyPreferences
227
+
228
+ # —————— Time table creator ——————
229
+ import json
230
+ from fastapi import Body
231
+ @app.post("/api/classrooms")
232
+ async def create_classroom(payload: ClassroomRequest = Body(...)):
233
+ """
234
+ Generate a detailed study timetable based on classroom parameters.
235
+ """
236
+ # ---------- Build prompt for Gemini 2.5 ----------
237
+ prefs = payload.studyPreferences
238
+ prompt = f"""
239
+ You are an expert academic coordinator.
240
+
241
+ Create a **4-week study timetable** for a classroom with the following settings:
242
+
243
+ - Subject: {payload.subject}
244
+ - Grade level: {payload.gradeLevel}
245
+ - Study days per week: {prefs.daysPerWeek}
246
+ - Hours per session: {prefs.hoursPerSession}
247
+ - Preferred learning style: {prefs.learningStyle}
248
+ - Role perspective: {payload.role}
249
+ {"- Textbook URL: " + payload.textbookUrl if payload.textbookUrl else ""}
250
+ {"- Syllabus URL: " + payload.syllabusUrl if payload.syllabusUrl else ""}
251
+
252
+ Requirements:
253
+
254
+ 1. Divide each week into exactly {prefs.daysPerWeek} sessions (label Day 1 … Day {prefs.daysPerWeek}).
255
+
256
+ 2. For **each session**, return:
257
+ - `week` (1-4)
258
+ - `day` (1-{prefs.daysPerWeek})
259
+ - `durationHours` (fixed: {prefs.hoursPerSession})
260
+ - `topic` (max 15 words)
261
+ - `activities` (array of 2-3 bullet strings)
262
+ - `materials` (array of links/titles; include textbook chapters if URL given)
263
+ - `homework` (concise task ≤ 30 words)
264
+
265
+ 3. **Output pure JSON only** using the schema:
266
+
267
+ ```json
268
+ {{
269
+ "classroom_id": "<same id as request>",
270
+ "timetable": [{{session objects as listed}}]
271
+ }}
272
+
273
+ Do not wrap JSON in markdown fences or commentary.
274
+ """
275
+ raw = gemini_flash_completion(prompt).strip()
276
+
277
+ # ---------- Attempt to parse JSON ----------
278
+ try:
279
+ timetable_json = json.loads(raw)
280
+ except json.JSONDecodeError:
281
+ logger.warning("Gemini returned invalid JSON; sending raw text.")
282
+ return JSONResponse(content={"classroom_id": payload.id, "timetable_raw": raw})
283
+ # Ensure id is echoed (fallback if model forgot)
284
+ timetable_json["classroom_id"] = payload.id
285
+ return JSONResponse(content=timetable_json)
286
+
287
+
288
+
289
+ # —————— Launch Server ———————
290
  if __name__ == "__main__":
291
  logger.info("✅ Launching FastAPI server...")
292
  try: