SarahXia0405 commited on
Commit
9506a5d
·
verified ·
1 Parent(s): 363fa19

Update api/server.py

Browse files
Files changed (1) hide show
  1. api/server.py +63 -24
api/server.py CHANGED
@@ -127,29 +127,32 @@ def _get_session(user_id: str) -> Dict[str, Any]:
127
  SESSIONS[user_id] = {
128
  "user_id": user_id,
129
  "name": "",
130
- "history": [],
131
  "weaknesses": [],
132
  "cognitive_state": {"confusion": 0, "mastery": 0},
133
  "course_outline": DEFAULT_COURSE_TOPICS,
134
  "rag_chunks": list(MODULE10_CHUNKS_CACHE),
135
  "model_name": DEFAULT_MODEL,
136
  "uploaded_files": [],
137
- # NEW: profile init (MVP in-memory)
138
  "profile_bio": "",
139
- "init_answers": {}, # raw answers
140
- "init_dismiss_until": 0, # unix ts seconds
141
  }
 
142
  if "uploaded_files" not in SESSIONS[user_id]:
143
  SESSIONS[user_id]["uploaded_files"] = []
144
- # backfill
 
145
  SESSIONS[user_id].setdefault("profile_bio", "")
146
  SESSIONS[user_id].setdefault("init_answers", {})
147
  SESSIONS[user_id].setdefault("init_dismiss_until", 0)
 
148
  return SESSIONS[user_id]
149
 
150
 
151
 
152
- # NEW: helper to build a deterministic “what files are loaded” hint for the LLM
153
  def _build_upload_hint(sess: Dict[str, Any]) -> str:
154
  files = sess.get("uploaded_files") or []
155
  if not files:
@@ -477,34 +480,51 @@ class ProfileDismissReq(BaseModel):
477
  user_id: str
478
  days: int = 7
479
 
 
480
  class ProfileInitSubmitReq(BaseModel):
481
  user_id: str
482
  answers: Dict[str, Any]
483
  language_preference: str = "Auto"
484
 
 
485
  def _generate_profile_bio_with_clare(
486
  sess: Dict[str, Any],
487
  answers: Dict[str, Any],
488
  language_preference: str = "Auto",
489
  ) -> str:
490
- # 生成时不要污染用户正常 history,用空 history
 
 
 
 
 
491
  prompt = f"""
492
  You are Clare, an AI teaching assistant.
493
- Task: Generate a short Profile Bio for the student based ONLY on the provided initialization answers.
494
 
495
- Rules:
 
 
 
 
496
  - Tone: neutral, supportive, non-judgmental.
497
  - No medical/psychological diagnosis language.
498
- - Do not infer sensitive attributes (race/religion/politics/health/sexuality).
499
- - Length: 80–160 Chinese characters if user seems Chinese; otherwise 60–120 English words.
500
- - Structure: (1) background & goal, (2) current skill level, (3) learning preferences, (4) how Clare will support.
 
 
 
 
 
 
501
 
502
- Student name (if any): {sess.get("name","")}
503
  Initialization answers (JSON):
504
  {answers}
 
 
505
  """.strip()
506
 
507
- resolved_lang = detect_language(prompt, language_preference)
508
 
509
  try:
510
  bio, _unused_history, _run_id = chat_with_clare(
@@ -517,13 +537,14 @@ Initialization answers (JSON):
517
  course_outline=sess["course_outline"],
518
  weaknesses=sess["weaknesses"],
519
  cognitive_state=sess["cognitive_state"],
520
- rag_context="", # profile init 不需要 RAG
521
  )
522
  return (bio or "").strip()
523
  except Exception as e:
524
  print("[profile_bio] generate failed:", repr(e))
525
  return ""
526
 
 
527
  # ----------------------------
528
  # API Routes
529
  # ----------------------------
@@ -957,6 +978,31 @@ def profile_status(user_id: str):
957
  }
958
 
959
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
960
  @app.post("/api/profile/dismiss")
961
  def profile_dismiss(req: ProfileDismissReq):
962
  user_id = (req.user_id or "").strip()
@@ -964,7 +1010,7 @@ def profile_dismiss(req: ProfileDismissReq):
964
  return JSONResponse({"error": "Missing user_id"}, status_code=400)
965
 
966
  sess = _get_session(user_id)
967
- days = max(1, min(int(req.days or 7), 30)) # 1–30
968
  sess["init_dismiss_until"] = int(time.time()) + days * 24 * 3600
969
  return {"ok": True, "dismissed_until": sess["init_dismiss_until"]}
970
 
@@ -978,22 +1024,15 @@ def profile_init_submit(req: ProfileInitSubmitReq):
978
  sess = _get_session(user_id)
979
  answers = req.answers or {}
980
 
981
- # save raw answers
982
  sess["init_answers"] = answers
983
 
984
- # generate bio
985
  bio = _generate_profile_bio_with_clare(sess, answers, req.language_preference)
986
-
987
  if not bio:
988
  return JSONResponse({"error": "Failed to generate bio"}, status_code=500)
989
 
990
  sess["profile_bio"] = bio
991
 
992
- return {
993
- "ok": True,
994
- "bio": bio,
995
- }
996
-
997
 
998
  # ----------------------------
999
  # SPA Fallback
 
127
  SESSIONS[user_id] = {
128
  "user_id": user_id,
129
  "name": "",
130
+ "history": [], # List[Tuple[str, str]]
131
  "weaknesses": [],
132
  "cognitive_state": {"confusion": 0, "mastery": 0},
133
  "course_outline": DEFAULT_COURSE_TOPICS,
134
  "rag_chunks": list(MODULE10_CHUNKS_CACHE),
135
  "model_name": DEFAULT_MODEL,
136
  "uploaded_files": [],
137
+ # NEW: profile init (MVP in-memory)
138
  "profile_bio": "",
139
+ "init_answers": {},
140
+ "init_dismiss_until": 0,
141
  }
142
+
143
  if "uploaded_files" not in SESSIONS[user_id]:
144
  SESSIONS[user_id]["uploaded_files"] = []
145
+
146
+ # NEW backfill
147
  SESSIONS[user_id].setdefault("profile_bio", "")
148
  SESSIONS[user_id].setdefault("init_answers", {})
149
  SESSIONS[user_id].setdefault("init_dismiss_until", 0)
150
+
151
  return SESSIONS[user_id]
152
 
153
 
154
 
155
+ # NEW: helper to build a deterministic “what files are loaded” hint for the LLM
156
  def _build_upload_hint(sess: Dict[str, Any]) -> str:
157
  files = sess.get("uploaded_files") or []
158
  if not files:
 
480
  user_id: str
481
  days: int = 7
482
 
483
+
484
  class ProfileInitSubmitReq(BaseModel):
485
  user_id: str
486
  answers: Dict[str, Any]
487
  language_preference: str = "Auto"
488
 
489
+
490
  def _generate_profile_bio_with_clare(
491
  sess: Dict[str, Any],
492
  answers: Dict[str, Any],
493
  language_preference: str = "Auto",
494
  ) -> str:
495
+ """
496
+ Generates an English Profile Bio. Keep it neutral/supportive and non-judgmental.
497
+ IMPORTANT: Do not contaminate user's normal chat history; use empty history.
498
+ """
499
+ student_name = (sess.get("name") or "").strip()
500
+
501
  prompt = f"""
502
  You are Clare, an AI teaching assistant.
 
503
 
504
+ Task:
505
+ Generate a concise English Profile Bio for the student using ONLY the initialization answers provided below.
506
+
507
+ Hard constraints:
508
+ - Output language: English.
509
  - Tone: neutral, supportive, non-judgmental.
510
  - No medical/psychological diagnosis language.
511
+ - Do not infer sensitive attributes (race, religion, political views, health status, sexuality, immigration status).
512
+ - Length: 60–120 words.
513
+ - Structure (4 short sentences max):
514
+ 1) background & current context
515
+ 2) learning goal for this course
516
+ 3) learning preferences (format + pace)
517
+ 4) how Clare will support them going forward (practical and concrete)
518
+
519
+ Student name (if available): {student_name}
520
 
 
521
  Initialization answers (JSON):
522
  {answers}
523
+
524
+ Return ONLY the bio text. Do not add a title.
525
  """.strip()
526
 
527
+ resolved_lang = "English" # force English regardless of UI preference
528
 
529
  try:
530
  bio, _unused_history, _run_id = chat_with_clare(
 
537
  course_outline=sess["course_outline"],
538
  weaknesses=sess["weaknesses"],
539
  cognitive_state=sess["cognitive_state"],
540
+ rag_context="",
541
  )
542
  return (bio or "").strip()
543
  except Exception as e:
544
  print("[profile_bio] generate failed:", repr(e))
545
  return ""
546
 
547
+
548
  # ----------------------------
549
  # API Routes
550
  # ----------------------------
 
978
  }
979
 
980
 
981
+
982
+
983
+ @app.get("/api/profile/status")
984
+ def profile_status(user_id: str):
985
+ user_id = (user_id or "").strip()
986
+ if not user_id:
987
+ return JSONResponse({"error": "Missing user_id"}, status_code=400)
988
+
989
+ sess = _get_session(user_id)
990
+ bio = (sess.get("profile_bio") or "").strip()
991
+ bio_len = len(bio)
992
+
993
+ now = int(time.time())
994
+ dismissed_until = int(sess.get("init_dismiss_until") or 0)
995
+
996
+ # Trigger if bio is too short and not within dismiss window
997
+ need_init = (bio_len <= 50) and (now >= dismissed_until)
998
+
999
+ return {
1000
+ "need_init": need_init,
1001
+ "bio_len": bio_len,
1002
+ "dismissed_until": dismissed_until,
1003
+ }
1004
+
1005
+
1006
  @app.post("/api/profile/dismiss")
1007
  def profile_dismiss(req: ProfileDismissReq):
1008
  user_id = (req.user_id or "").strip()
 
1010
  return JSONResponse({"error": "Missing user_id"}, status_code=400)
1011
 
1012
  sess = _get_session(user_id)
1013
+ days = max(1, min(int(req.days or 7), 30)) # 1–30 days
1014
  sess["init_dismiss_until"] = int(time.time()) + days * 24 * 3600
1015
  return {"ok": True, "dismissed_until": sess["init_dismiss_until"]}
1016
 
 
1024
  sess = _get_session(user_id)
1025
  answers = req.answers or {}
1026
 
 
1027
  sess["init_answers"] = answers
1028
 
 
1029
  bio = _generate_profile_bio_with_clare(sess, answers, req.language_preference)
 
1030
  if not bio:
1031
  return JSONResponse({"error": "Failed to generate bio"}, status_code=500)
1032
 
1033
  sess["profile_bio"] = bio
1034
 
1035
+ return {"ok": True, "bio": bio}
 
 
 
 
1036
 
1037
  # ----------------------------
1038
  # SPA Fallback