SarahXia0405 commited on
Commit
96c3617
·
verified ·
1 Parent(s): 14f19ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -61
app.py CHANGED
@@ -5,7 +5,7 @@ from collections import defaultdict
5
  from typing import List, Dict
6
 
7
  import gradio as gr
8
- from langsmith import Client # <<< 新增:LangSmith 客户端
9
 
10
  from config import (
11
  DEFAULT_MODEL,
@@ -32,7 +32,6 @@ from rag_engine import (
32
  from syllabus_utils import extract_course_topics_from_file
33
 
34
  # ================== Assets ==================
35
- # HANBRIDGE_LOGO_PATH = "hanbridge_logo.png"
36
  CLARE_LOGO_PATH = "clare_mascot.png"
37
  CLARE_RUN_PATH = "Clare_Run.png"
38
  CLARE_READING_PATH = "Clare_reading.png" # 确保存在
@@ -264,17 +263,18 @@ else:
264
 
265
  # ===== LangSmith logging =====
266
  ls_client = Client()
267
- LS_DATASET_NAME = "clare_user_events" # 你可以在 LangSmith 里看到这个 dataset 名字
 
268
 
269
  def log_event(data: Dict):
270
  """
271
- 把日志写入 LangSmith 的一个 datasetclare_user_events
272
- 每个事件是一条 example,包含 inputs / outputs / metadata
273
  """
274
  try:
275
  inputs = {
276
- "question": data.get("question"),
277
  "event_type": data.get("event_type"),
 
278
  "student_id": data.get("student_id"),
279
  }
280
  outputs = {
@@ -295,6 +295,7 @@ def log_event(data: Dict):
295
  except Exception as e:
296
  print("LangSmith log failed:", e)
297
 
 
298
  # ===== Reference Formatting Helper =====
299
  def format_references(
300
  rag_chunks: List[Dict], max_files: int = 2, max_sections_per_file: int = 3
@@ -344,7 +345,7 @@ def is_academic_query(message: str) -> bool:
344
  "thanks", "thank", "thank you",
345
  "ok", "okay",
346
  "bye", "goodbye", "see you",
347
- "haha", "lol"
348
  }
349
  tokens = m.split()
350
 
@@ -381,17 +382,17 @@ with gr.Blocks(
381
  title="Clare – Hanbridge AI Teaching Assistant", css=CUSTOM_CSS
382
  ) as demo:
383
 
384
- # 全局状态(预加载 Module 10 作为基础)
385
  course_outline_state = gr.State(preloaded_topics or DEFAULT_COURSE_TOPICS)
386
  weakness_state = gr.State([])
387
  cognitive_state_state = gr.State({"confusion": 0, "mastery": 0})
388
  rag_chunks_state = gr.State(preloaded_chunks or [])
389
 
390
- # 最近一次回答(用于每条回答的 thumbs / 详细反馈)
391
  last_question_state = gr.State("")
392
  last_answer_state = gr.State("")
393
 
394
- # 用户状态(登录)
395
  user_name_state = gr.State("")
396
  user_id_state = gr.State("")
397
 
@@ -508,12 +509,18 @@ with gr.Blocks(
508
  gr.Markdown(USER_GUIDE_SECTIONS["faq"])
509
 
510
  gr.Markdown("---")
511
- gr.Button("System Settings", size="sm", variant="secondary", interactive=False)
 
 
 
 
 
512
 
513
  gr.HTML(
514
  """
515
  <div style="font-size: 11px; color: #9ca3af; margin-top: 15px; text-align: left;">
516
- © 2025 Made by <a href="https://www.linkedin.com/in/qinghua-xia-479199252/" target="_blank" style="color: #6b7280; text-decoration: underline;">Sarah Xia</a>
 
517
  </div>
518
  """
519
  )
@@ -521,7 +528,6 @@ with gr.Blocks(
521
  # === Center Main ===
522
  with gr.Column(scale=3):
523
 
524
- # Instruction + Chat
525
  gr.Markdown(
526
  """
527
  <div style="background-color:#f9fafb; padding:10px; border-radius:5px; margin-top:10px; font-size:0.9em; color:#555;">
@@ -532,57 +538,53 @@ with gr.Blocks(
532
  """
533
  )
534
 
535
-
536
  chatbot = gr.Chatbot(
537
  label="",
538
  height=450,
539
  avatar_images=(None, CLARE_LOGO_PATH),
540
  show_label=False,
541
- type="tuples", # 你现在的 history 是 list[ [user, assistant], ... ],保留 tuples 就行
542
- likeable=True, # 新版本才支持
543
- )
544
-
545
- def handle_like(data, history, user_id, mode_val, model_name_val, lang_pref):
546
- """
547
- data 里一般会带:
548
- - data["index"] 被点的是第几条对话
549
- - data["value"] 是 "like" / "dislike"
550
- 你可以用这个信息去调用 LangSmith 的 feedback API
551
- """
552
- idx = data.get("index", -1)
553
- value = data.get("value", "")
554
- if idx < 0 or idx >= len(history):
555
- return
556
-
557
- question, answer = history[idx]
558
-
559
- # 在这里把 question/answer + value 发到 LangSmith
560
- # (用你之前已经写好的 record_feedback / client.create_feedback 那一套)
561
- # record_langsmith_feedback(question, answer, value, user_id, mode_val, model_name_val, lang_pref)
562
-
563
- print(f"[LIKE EVENT] idx={idx}, value={value}")
564
-
565
- chatbot.like(
566
- fn=handle_like,
567
- inputs=[chatbot, user_id_state, learning_mode, model_name, language_preference],
568
- outputs=[],
569
  )
570
 
 
 
 
 
 
 
 
 
 
571
 
 
 
 
 
 
 
572
  feedback_text = gr.Textbox(
573
  label="What worked well or what was wrong?",
574
- placeholder="Optional: describe what you liked / what was confusing or incorrect.",
 
 
575
  lines=3,
576
  visible=False,
577
  )
578
  feedback_submit_btn = gr.Button(
579
- "Submit Feedback", size="sm", variant="primary", visible=False, interactive=False
 
 
 
 
580
  )
581
 
582
  # 用户输入
583
  user_input = gr.Textbox(
584
  label="Your Input",
585
- placeholder="Please log in on the right before asking Clare anything...",
 
 
586
  show_label=False,
587
  container=True,
588
  autofocus=False,
@@ -596,7 +598,9 @@ with gr.Blocks(
596
  file_types=[".docx", ".pdf", ".pptx"],
597
  file_count="single",
598
  height=160,
599
- label="Upload additional Module 10 file (.docx/.pdf/.pptx) — optional",
 
 
600
  interactive=False,
601
  )
602
  with gr.Column(scale=1):
@@ -669,13 +673,22 @@ with gr.Blocks(
669
 
670
  gr.Markdown("### Actions")
671
  export_btn = gr.Button(
672
- "Export Conversation", size="sm", elem_classes="action-btn", interactive=False
 
 
 
673
  )
674
  quiz_btn = gr.Button(
675
- "Let's Try (Micro-Quiz)", size="sm", elem_classes="action-btn", interactive=False
 
 
 
676
  )
677
  summary_btn = gr.Button(
678
- "Summarization", size="sm", elem_classes="action-btn", interactive=False
 
 
 
679
  )
680
 
681
  gr.Markdown("### Results")
@@ -705,7 +718,10 @@ with gr.Blocks(
705
  login_state_2: gr.update(),
706
  login_state_3: gr.update(),
707
  student_info_html: gr.update(
708
- value="<p style='color:red; font-size:12px;'>Please enter both Name and Email/ID to start.</p>"
 
 
 
709
  ),
710
  user_name_state: gr.update(),
711
  user_id_state: gr.update(),
@@ -743,7 +759,9 @@ with gr.Blocks(
743
  user_id_state: id_val,
744
  user_input: gr.update(
745
  interactive=True,
746
- placeholder="Ask about Module 10 concepts, Responsible AI, or let Clare test you...",
 
 
747
  ),
748
  clear_btn: gr.update(interactive=True),
749
  export_btn: gr.update(interactive=True),
@@ -806,7 +824,9 @@ with gr.Blocks(
806
  user_input: gr.update(
807
  value="",
808
  interactive=False,
809
- placeholder="Please log in on the right before asking Clare anything...",
 
 
810
  ),
811
  clear_btn: gr.update(interactive=False),
812
  export_btn: gr.update(interactive=False),
@@ -897,7 +917,7 @@ with gr.Blocks(
897
 
898
  def show_loaded_docs(doc_type_val):
899
  gr.Info(
900
- f"For this experiment, Clare always includes the pre-loaded Module 10 reading.\n"
901
  f"Additional uploaded {doc_type_val} files will be used as supplementary context.",
902
  title="Loaded Documents",
903
  )
@@ -917,7 +937,7 @@ with gr.Blocks(
917
  doc_type_val,
918
  user_id_val,
919
  ):
920
- # 双保险:没登录就提示
921
  if not user_id_val:
922
  out_msg = (
923
  "🔒 Please log in with your Student Name and Email/ID on the right "
@@ -941,7 +961,6 @@ with gr.Blocks(
941
  )
942
  return "", chat_history, weaknesses, cognitive_state, new_status, "", ""
943
 
944
- # 更新学生状态
945
  weaknesses = update_weaknesses_from_message(message, weaknesses or [])
946
  cognitive_state = update_cognitive_state_from_message(message, cognitive_state)
947
 
@@ -1005,7 +1024,7 @@ with gr.Blocks(
1005
 
1006
  new_status = render_session_status(mode_val, weaknesses, cognitive_state)
1007
 
1008
- # 将当前这一轮的 Q/A 存入 state,后面 thumbs / 详细反馈用
1009
  return "", new_history, weaknesses, cognitive_state, new_status, message, answer
1010
 
1011
  user_input.submit(
@@ -1072,8 +1091,7 @@ with gr.Blocks(
1072
  "• Do NOT start a content question until I have answered 1 or 2.\n\n"
1073
  "Step 2 – After I choose the style:\n"
1074
  "• If I choose 1 (multiple-choice):\n"
1075
- " - Ask ONE multiple-choice question at a time, based on Module 10 concepts "
1076
- "(Responsible AI definition, risk types, mitigation layers, EU AI Act, etc.).\n"
1077
  " - Provide 3–4 options (A, B, C, D) and make only one option clearly correct.\n"
1078
  "• If I choose 2 (short-answer):\n"
1079
  " - Ask ONE short-answer question at a time, also based on Module 10 concepts.\n"
@@ -1185,7 +1203,9 @@ with gr.Blocks(
1185
  except Exception as e:
1186
  print("thumb_up log error:", e)
1187
 
1188
- def send_thumb_down(last_q, last_a, user_id_val, mode_val, model_name_val, lang_pref):
 
 
1189
  if not last_q and not last_a:
1190
  print("No last QA to log for thumbs_down.")
1191
  return
@@ -1283,7 +1303,13 @@ with gr.Blocks(
1283
  # ===== Export / Summary =====
1284
  export_btn.click(
1285
  lambda h, c, m, w, cog: export_conversation(h, c, m, w, cog),
1286
- [chatbot, course_outline_state, learning_mode, weakness_state, cognitive_state_state],
 
 
 
 
 
 
1287
  [result_display],
1288
  )
1289
 
@@ -1326,7 +1352,7 @@ with gr.Blocks(
1326
 
1327
  if __name__ == "__main__":
1328
  demo.launch(
1329
- share=True,
1330
  server_name="0.0.0.0",
1331
  server_port=7860,
1332
  )
 
5
  from typing import List, Dict
6
 
7
  import gradio as gr
8
+ from langsmith import Client
9
 
10
  from config import (
11
  DEFAULT_MODEL,
 
32
  from syllabus_utils import extract_course_topics_from_file
33
 
34
  # ================== Assets ==================
 
35
  CLARE_LOGO_PATH = "clare_mascot.png"
36
  CLARE_RUN_PATH = "Clare_Run.png"
37
  CLARE_READING_PATH = "Clare_reading.png" # 确保存在
 
263
 
264
  # ===== LangSmith logging =====
265
  ls_client = Client()
266
+ LS_DATASET_NAME = "clare_user_events"
267
+
268
 
269
  def log_event(data: Dict):
270
  """
271
+ 写入 LangSmith datasetclare_user_events
272
+ 每个事件是一条 exampleinputs / outputs / metadata
273
  """
274
  try:
275
  inputs = {
 
276
  "event_type": data.get("event_type"),
277
+ "question": data.get("question"),
278
  "student_id": data.get("student_id"),
279
  }
280
  outputs = {
 
295
  except Exception as e:
296
  print("LangSmith log failed:", e)
297
 
298
+
299
  # ===== Reference Formatting Helper =====
300
  def format_references(
301
  rag_chunks: List[Dict], max_files: int = 2, max_sections_per_file: int = 3
 
345
  "thanks", "thank", "thank you",
346
  "ok", "okay",
347
  "bye", "goodbye", "see you",
348
+ "haha", "lol",
349
  }
350
  tokens = m.split()
351
 
 
382
  title="Clare – Hanbridge AI Teaching Assistant", css=CUSTOM_CSS
383
  ) as demo:
384
 
385
+ # 全局状态
386
  course_outline_state = gr.State(preloaded_topics or DEFAULT_COURSE_TOPICS)
387
  weakness_state = gr.State([])
388
  cognitive_state_state = gr.State({"confusion": 0, "mastery": 0})
389
  rag_chunks_state = gr.State(preloaded_chunks or [])
390
 
391
+ # 最近一次回答(用于 thumbs / 详细反馈)
392
  last_question_state = gr.State("")
393
  last_answer_state = gr.State("")
394
 
395
+ # 用户登录状态
396
  user_name_state = gr.State("")
397
  user_id_state = gr.State("")
398
 
 
509
  gr.Markdown(USER_GUIDE_SECTIONS["faq"])
510
 
511
  gr.Markdown("---")
512
+ gr.Button(
513
+ "System Settings",
514
+ size="sm",
515
+ variant="secondary",
516
+ interactive=False,
517
+ )
518
 
519
  gr.HTML(
520
  """
521
  <div style="font-size: 11px; color: #9ca3af; margin-top: 15px; text-align: left;">
522
+ © 2025 Made by <a href="https://www.linkedin.com/in/qinghua-xia-479199252/"
523
+ target="_blank" style="color: #6b7280; text-decoration: underline;">Sarah Xia</a>
524
  </div>
525
  """
526
  )
 
528
  # === Center Main ===
529
  with gr.Column(scale=3):
530
 
 
531
  gr.Markdown(
532
  """
533
  <div style="background-color:#f9fafb; padding:10px; border-radius:5px; margin-top:10px; font-size:0.9em; color:#555;">
 
538
  """
539
  )
540
 
 
541
  chatbot = gr.Chatbot(
542
  label="",
543
  height=450,
544
  avatar_images=(None, CLARE_LOGO_PATH),
545
  show_label=False,
546
+ bubble_full_width=False, # 只是 warning,不影响
547
+ type="tuples",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  )
549
 
550
+ # === Feedback on last answer ===
551
+ gr.Markdown("#### Rate Clare’s last answer")
552
+ with gr.Row():
553
+ thumb_up_btn = gr.Button(
554
+ "👍 Helpful", size="sm", interactive=False
555
+ )
556
+ thumb_down_btn = gr.Button(
557
+ "👎 Not helpful", size="sm", interactive=False
558
+ )
559
 
560
+ feedback_toggle_btn = gr.Button(
561
+ "Give detailed feedback",
562
+ size="sm",
563
+ variant="secondary",
564
+ interactive=False,
565
+ )
566
  feedback_text = gr.Textbox(
567
  label="What worked well or what was wrong?",
568
+ placeholder=(
569
+ "Optional: describe what you liked / what was confusing or incorrect."
570
+ ),
571
  lines=3,
572
  visible=False,
573
  )
574
  feedback_submit_btn = gr.Button(
575
+ "Submit Feedback",
576
+ size="sm",
577
+ variant="primary",
578
+ visible=False,
579
+ interactive=False,
580
  )
581
 
582
  # 用户输入
583
  user_input = gr.Textbox(
584
  label="Your Input",
585
+ placeholder=(
586
+ "Please log in on the right before asking Clare anything..."
587
+ ),
588
  show_label=False,
589
  container=True,
590
  autofocus=False,
 
598
  file_types=[".docx", ".pdf", ".pptx"],
599
  file_count="single",
600
  height=160,
601
+ label=(
602
+ "Upload additional Module 10 file (.docx/.pdf/.pptx) — optional"
603
+ ),
604
  interactive=False,
605
  )
606
  with gr.Column(scale=1):
 
673
 
674
  gr.Markdown("### Actions")
675
  export_btn = gr.Button(
676
+ "Export Conversation",
677
+ size="sm",
678
+ elem_classes="action-btn",
679
+ interactive=False,
680
  )
681
  quiz_btn = gr.Button(
682
+ "Let's Try (Micro-Quiz)",
683
+ size="sm",
684
+ elem_classes="action-btn",
685
+ interactive=False,
686
  )
687
  summary_btn = gr.Button(
688
+ "Summarization",
689
+ size="sm",
690
+ elem_classes="action-btn",
691
+ interactive=False,
692
  )
693
 
694
  gr.Markdown("### Results")
 
718
  login_state_2: gr.update(),
719
  login_state_3: gr.update(),
720
  student_info_html: gr.update(
721
+ value=(
722
+ "<p style='color:red; font-size:12px;'>Please enter both "
723
+ "Name and Email/ID to start.</p>"
724
+ )
725
  ),
726
  user_name_state: gr.update(),
727
  user_id_state: gr.update(),
 
759
  user_id_state: id_val,
760
  user_input: gr.update(
761
  interactive=True,
762
+ placeholder=(
763
+ "Ask about Module 10 concepts, Responsible AI, or let Clare test you..."
764
+ ),
765
  ),
766
  clear_btn: gr.update(interactive=True),
767
  export_btn: gr.update(interactive=True),
 
824
  user_input: gr.update(
825
  value="",
826
  interactive=False,
827
+ placeholder=(
828
+ "Please log in on the right before asking Clare anything..."
829
+ ),
830
  ),
831
  clear_btn: gr.update(interactive=False),
832
  export_btn: gr.update(interactive=False),
 
917
 
918
  def show_loaded_docs(doc_type_val):
919
  gr.Info(
920
+ "For this experiment, Clare always includes the pre-loaded Module 10 reading.\n"
921
  f"Additional uploaded {doc_type_val} files will be used as supplementary context.",
922
  title="Loaded Documents",
923
  )
 
937
  doc_type_val,
938
  user_id_val,
939
  ):
940
+ # 没登录就提示
941
  if not user_id_val:
942
  out_msg = (
943
  "🔒 Please log in with your Student Name and Email/ID on the right "
 
961
  )
962
  return "", chat_history, weaknesses, cognitive_state, new_status, "", ""
963
 
 
964
  weaknesses = update_weaknesses_from_message(message, weaknesses or [])
965
  cognitive_state = update_cognitive_state_from_message(message, cognitive_state)
966
 
 
1024
 
1025
  new_status = render_session_status(mode_val, weaknesses, cognitive_state)
1026
 
1027
+ # 将当前这一轮的 Q/A 存入 state,供 thumbs / 文字反馈使用
1028
  return "", new_history, weaknesses, cognitive_state, new_status, message, answer
1029
 
1030
  user_input.submit(
 
1091
  "• Do NOT start a content question until I have answered 1 or 2.\n\n"
1092
  "Step 2 – After I choose the style:\n"
1093
  "• If I choose 1 (multiple-choice):\n"
1094
+ " - Ask ONE multiple-choice question at a time, based on Module 10 concepts.\n"
 
1095
  " - Provide 3–4 options (A, B, C, D) and make only one option clearly correct.\n"
1096
  "• If I choose 2 (short-answer):\n"
1097
  " - Ask ONE short-answer question at a time, also based on Module 10 concepts.\n"
 
1203
  except Exception as e:
1204
  print("thumb_up log error:", e)
1205
 
1206
+ def send_thumb_down(
1207
+ last_q, last_a, user_id_val, mode_val, model_name_val, lang_pref
1208
+ ):
1209
  if not last_q and not last_a:
1210
  print("No last QA to log for thumbs_down.")
1211
  return
 
1303
  # ===== Export / Summary =====
1304
  export_btn.click(
1305
  lambda h, c, m, w, cog: export_conversation(h, c, m, w, cog),
1306
+ [
1307
+ chatbot,
1308
+ course_outline_state,
1309
+ learning_mode,
1310
+ weakness_state,
1311
+ cognitive_state_state,
1312
+ ],
1313
  [result_display],
1314
  )
1315
 
 
1352
 
1353
  if __name__ == "__main__":
1354
  demo.launch(
1355
+ share=True, # HF Space 环境需要 share=True 才能访问
1356
  server_name="0.0.0.0",
1357
  server_port=7860,
1358
  )