SarahXia0405 commited on
Commit
cca32d6
·
verified ·
1 Parent(s): 053aa9f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +118 -95
app.py CHANGED
@@ -1,11 +1,11 @@
1
  import os
2
  import time
3
  import base64
4
- import requests
5
  from collections import defaultdict
6
- from typing import List, Dict, Tuple, Optional
7
 
8
  import gradio as gr
 
9
 
10
  from config import (
11
  DEFAULT_MODEL,
@@ -262,39 +262,43 @@ if os.path.exists(MODULE10_PATH):
262
  else:
263
  print("Module 10 PDF not found at path:", MODULE10_PATH)
264
 
265
- # ===== Google Sheet logging ====
266
- GSHEET_WEBHOOK_URL = os.getenv("GSHEET_WEBHOOK_URL")
267
- GSHEET_API_KEY = os.getenv("GSHEET_API_KEY")
268
-
269
 
270
  def log_event(data: Dict):
271
  """
272
- 把日志发到 Google Sheet(通过 Apps Script Webhook
 
273
  """
274
- if not GSHEET_WEBHOOK_URL:
275
- print("Google Sheet webhook not configured, skip logging")
276
- return
277
-
278
- payload = data.copy()
279
- payload["api_key"] = GSHEET_API_KEY or ""
280
-
281
  try:
282
- resp = requests.post(GSHEET_WEBHOOK_URL, json=payload, timeout=3)
283
- if not resp.ok:
284
- print("GSheet log failed:", resp.status_code, resp.text)
285
- except Exception as e:
286
- print("GSheet log exception:", e)
 
 
 
 
 
 
 
 
287
 
 
 
 
 
 
 
 
 
288
 
289
  # ===== Reference Formatting Helper =====
290
  def format_references(
291
  rag_chunks: List[Dict], max_files: int = 2, max_sections_per_file: int = 3
292
  ) -> str:
293
- """
294
- 根据当前使用的 rag_chunks,生成一个简短的 reference 列表:
295
- - 按文件名分组
296
- - 每个文件列出若干个 section
297
- """
298
  if not rag_chunks:
299
  return ""
300
 
@@ -326,9 +330,6 @@ def format_references(
326
 
327
 
328
  def is_academic_query(message: str) -> bool:
329
- """
330
- 判断当前学生输入是否是“学术/课程相关”问题。
331
- """
332
  if not message:
333
  return False
334
 
@@ -338,7 +339,6 @@ def is_academic_query(message: str) -> bool:
338
 
339
  m = " ".join(m.split())
340
 
341
- # 1) 典型闲聊词
342
  smalltalk_tokens = {
343
  "hi", "hello", "hey", "yo",
344
  "thanks", "thank", "thank you",
@@ -351,7 +351,6 @@ def is_academic_query(message: str) -> bool:
351
  if "?" not in m and all(t in smalltalk_tokens for t in tokens):
352
  return False
353
 
354
- # 2) 自我介绍 / 工具说明
355
  meta_phrases = [
356
  "who are you",
357
  "what are you",
@@ -371,7 +370,6 @@ def is_academic_query(message: str) -> bool:
371
  if any(p in m for p in meta_phrases):
372
  return False
373
 
374
- # 3) 很短又没有问号,大概率不是学术问题
375
  if len(tokens) <= 2 and "?" not in m:
376
  return False
377
 
@@ -389,7 +387,7 @@ with gr.Blocks(
389
  cognitive_state_state = gr.State({"confusion": 0, "mastery": 0})
390
  rag_chunks_state = gr.State(preloaded_chunks or [])
391
 
392
- # 最近一次回答(用于详细反馈)
393
  last_question_state = gr.State("")
394
  last_answer_state = gr.State("")
395
 
@@ -522,7 +520,7 @@ with gr.Blocks(
522
 
523
  # === Center Main ===
524
  with gr.Column(scale=3):
525
-
526
  # Instruction + Chat
527
  gr.Markdown(
528
  """
@@ -533,16 +531,16 @@ with gr.Blocks(
533
  </div>
534
  """
535
  )
536
-
537
  chatbot = gr.Chatbot(
538
  label="",
539
  height=450,
540
  avatar_images=(None, CLARE_LOGO_PATH),
541
  show_label=False,
542
- bubble_full_width=False, # 未来会被忽略,但保留不影响
543
- type="tuples", # ✅ 关键:我们用的是 [(user, assistant), ...] 的格式
544
  )
545
-
546
  # === Feedback on last answer ===
547
  gr.Markdown("#### Rate Clare’s last answer")
548
  with gr.Row():
@@ -552,7 +550,7 @@ with gr.Blocks(
552
  thumb_down_btn = gr.Button(
553
  "👎 Not helpful", size="sm", interactive=False
554
  )
555
-
556
  feedback_toggle_btn = gr.Button(
557
  "Give detailed feedback", size="sm", variant="secondary", interactive=False
558
  )
@@ -566,8 +564,6 @@ with gr.Blocks(
566
  "Submit Feedback", size="sm", variant="primary", visible=False, interactive=False
567
  )
568
 
569
-
570
-
571
  # 用户输入
572
  user_input = gr.Textbox(
573
  label="Your Input",
@@ -710,6 +706,8 @@ with gr.Blocks(
710
  learning_mode: gr.update(interactive=False),
711
  model_name: gr.update(interactive=False),
712
  docs_btn: gr.update(interactive=False),
 
 
713
  feedback_toggle_btn: gr.update(interactive=False),
714
  feedback_text: gr.update(visible=False, value=""),
715
  feedback_submit_btn: gr.update(interactive=False, visible=False),
@@ -743,6 +741,8 @@ with gr.Blocks(
743
  learning_mode: gr.update(interactive=True),
744
  model_name: gr.update(interactive=False),
745
  docs_btn: gr.update(interactive=True),
 
 
746
  feedback_toggle_btn: gr.update(interactive=True),
747
  feedback_text: gr.update(visible=False, value=""),
748
  feedback_submit_btn: gr.update(interactive=True, visible=False),
@@ -770,6 +770,8 @@ with gr.Blocks(
770
  learning_mode,
771
  model_name,
772
  docs_btn,
 
 
773
  feedback_toggle_btn,
774
  feedback_text,
775
  feedback_submit_btn,
@@ -801,6 +803,8 @@ with gr.Blocks(
801
  language_preference: gr.update(interactive=False),
802
  learning_mode: gr.update(interactive=False),
803
  docs_btn: gr.update(interactive=False),
 
 
804
  feedback_toggle_btn: gr.update(interactive=False),
805
  feedback_text: gr.update(visible=False, value=""),
806
  feedback_submit_btn: gr.update(interactive=False, visible=False),
@@ -828,6 +832,8 @@ with gr.Blocks(
828
  language_preference,
829
  learning_mode,
830
  docs_btn,
 
 
831
  feedback_toggle_btn,
832
  feedback_text,
833
  feedback_submit_btn,
@@ -961,7 +967,7 @@ with gr.Blocks(
961
  new_history[-1] = [last_user, last_assistant]
962
  answer = last_assistant
963
 
964
- # 日志
965
  student_id = user_id_val or "ANON"
966
  experiment_id = "RESP_AI_W10"
967
  try:
@@ -984,7 +990,7 @@ with gr.Blocks(
984
 
985
  new_status = render_session_status(mode_val, weaknesses, cognitive_state)
986
 
987
- # 将当前这一轮的 Q/A 存入 state,详细反馈用(针对“最后一条回答”)
988
  return "", new_history, weaknesses, cognitive_state, new_status, message, answer
989
 
990
  user_input.submit(
@@ -1131,9 +1137,6 @@ with gr.Blocks(
1131
 
1132
  # ===== Feedback Handlers =====
1133
  def show_feedback_box():
1134
- """
1135
- 点击 'Give detailed feedback' 时显示文本框 + 提交按钮
1136
- """
1137
  return {
1138
  feedback_text: gr.update(visible=True),
1139
  feedback_submit_btn: gr.update(visible=True),
@@ -1145,11 +1148,80 @@ with gr.Blocks(
1145
  [feedback_text, feedback_submit_btn],
1146
  )
1147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1148
  def submit_detailed_feedback(
1149
  text, last_q, last_a, user_id_val, mode_val, model_name_val, lang_pref
1150
  ):
1151
  if not text or not text.strip():
1152
- # 提示:请先填写再提交(用 placeholder 提醒)
1153
  return gr.update(
1154
  value="",
1155
  placeholder="Please enter some feedback before submitting.",
@@ -1170,11 +1242,10 @@ with gr.Blocks(
1170
  "learning_mode": mode_val,
1171
  }
1172
  )
1173
- print("[Feedback] detailed_feedback logged.")
1174
  except Exception as e:
1175
  print("detailed_feedback log error:", e)
1176
 
1177
- # 清空文本框,并显示感谢(用 placeholder)
1178
  return gr.update(
1179
  value="",
1180
  placeholder="Thanks! Your feedback has been recorded.",
@@ -1194,54 +1265,6 @@ with gr.Blocks(
1194
  [feedback_text],
1195
  )
1196
 
1197
- # ===== Per-message thumbs (像 ChatGPT 一样) =====
1198
- def handle_like(
1199
- data: gr.LikeData,
1200
- history,
1201
- user_id_val,
1202
- mode_val,
1203
- model_name_val,
1204
- lang_pref,
1205
- ):
1206
- """
1207
- data: gr.LikeData (自动注入)
1208
- history: Chatbot 历史 (list of [user, assistant])
1209
- """
1210
- if data is None or history is None:
1211
- return
1212
-
1213
- idx = data.index
1214
- if not isinstance(idx, int) or idx < 0 or idx >= len(history):
1215
- return
1216
-
1217
- question, answer = history[idx]
1218
- event_type = "thumbs_up" if data.liked else "thumbs_down"
1219
-
1220
- try:
1221
- log_event(
1222
- {
1223
- "experiment_id": "RESP_AI_W10",
1224
- "student_id": user_id_val or "ANON",
1225
- "event_type": event_type,
1226
- "timestamp": time.time(),
1227
- "question": question,
1228
- "answer": answer,
1229
- "model_name": model_name_val,
1230
- "language": lang_pref,
1231
- "learning_mode": mode_val,
1232
- "turn_index": idx,
1233
- }
1234
- )
1235
- print(f"[Feedback] {event_type} logged for turn {idx}.")
1236
- except Exception as e:
1237
- print("like log error:", e)
1238
-
1239
- chatbot.like(
1240
- handle_like,
1241
- inputs=[chatbot, user_id_state, learning_mode, model_name, language_preference],
1242
- outputs=[],
1243
- )
1244
-
1245
  # ===== Export / Summary =====
1246
  export_btn.click(
1247
  lambda h, c, m, w, cog: export_conversation(h, c, m, w, cog),
 
1
  import os
2
  import time
3
  import base64
 
4
  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,
 
262
  else:
263
  print("Module 10 PDF not found at path:", MODULE10_PATH)
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 的一个 dataset(clare_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 = {
281
+ "answer": data.get("answer"),
282
+ }
283
+ metadata = {
284
+ k: v
285
+ for k, v in data.items()
286
+ if k not in ("question", "answer", "event_type")
287
+ }
288
 
289
+ ls_client.create_example(
290
+ inputs=inputs,
291
+ outputs=outputs,
292
+ metadata=metadata,
293
+ dataset_name=LS_DATASET_NAME,
294
+ )
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
301
  ) -> str:
 
 
 
 
 
302
  if not rag_chunks:
303
  return ""
304
 
 
330
 
331
 
332
  def is_academic_query(message: str) -> bool:
 
 
 
333
  if not message:
334
  return False
335
 
 
339
 
340
  m = " ".join(m.split())
341
 
 
342
  smalltalk_tokens = {
343
  "hi", "hello", "hey", "yo",
344
  "thanks", "thank", "thank you",
 
351
  if "?" not in m and all(t in smalltalk_tokens for t in tokens):
352
  return False
353
 
 
354
  meta_phrases = [
355
  "who are you",
356
  "what are you",
 
370
  if any(p in m for p in meta_phrases):
371
  return False
372
 
 
373
  if len(tokens) <= 2 and "?" not in m:
374
  return False
375
 
 
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
 
 
520
 
521
  # === Center Main ===
522
  with gr.Column(scale=3):
523
+
524
  # Instruction + Chat
525
  gr.Markdown(
526
  """
 
531
  </div>
532
  """
533
  )
534
+
535
  chatbot = gr.Chatbot(
536
  label="",
537
  height=450,
538
  avatar_images=(None, CLARE_LOGO_PATH),
539
  show_label=False,
540
+ bubble_full_width=False,
541
+ type="tuples",
542
  )
543
+
544
  # === Feedback on last answer ===
545
  gr.Markdown("#### Rate Clare’s last answer")
546
  with gr.Row():
 
550
  thumb_down_btn = gr.Button(
551
  "👎 Not helpful", size="sm", interactive=False
552
  )
553
+
554
  feedback_toggle_btn = gr.Button(
555
  "Give detailed feedback", size="sm", variant="secondary", interactive=False
556
  )
 
564
  "Submit Feedback", size="sm", variant="primary", visible=False, interactive=False
565
  )
566
 
 
 
567
  # 用户输入
568
  user_input = gr.Textbox(
569
  label="Your Input",
 
706
  learning_mode: gr.update(interactive=False),
707
  model_name: gr.update(interactive=False),
708
  docs_btn: gr.update(interactive=False),
709
+ thumb_up_btn: gr.update(interactive=False),
710
+ thumb_down_btn: gr.update(interactive=False),
711
  feedback_toggle_btn: gr.update(interactive=False),
712
  feedback_text: gr.update(visible=False, value=""),
713
  feedback_submit_btn: gr.update(interactive=False, visible=False),
 
741
  learning_mode: gr.update(interactive=True),
742
  model_name: gr.update(interactive=False),
743
  docs_btn: gr.update(interactive=True),
744
+ thumb_up_btn: gr.update(interactive=True),
745
+ thumb_down_btn: gr.update(interactive=True),
746
  feedback_toggle_btn: gr.update(interactive=True),
747
  feedback_text: gr.update(visible=False, value=""),
748
  feedback_submit_btn: gr.update(interactive=True, visible=False),
 
770
  learning_mode,
771
  model_name,
772
  docs_btn,
773
+ thumb_up_btn,
774
+ thumb_down_btn,
775
  feedback_toggle_btn,
776
  feedback_text,
777
  feedback_submit_btn,
 
803
  language_preference: gr.update(interactive=False),
804
  learning_mode: gr.update(interactive=False),
805
  docs_btn: gr.update(interactive=False),
806
+ thumb_up_btn: gr.update(interactive=False),
807
+ thumb_down_btn: gr.update(interactive=False),
808
  feedback_toggle_btn: gr.update(interactive=False),
809
  feedback_text: gr.update(visible=False, value=""),
810
  feedback_submit_btn: gr.update(interactive=False, visible=False),
 
832
  language_preference,
833
  learning_mode,
834
  docs_btn,
835
+ thumb_up_btn,
836
+ thumb_down_btn,
837
  feedback_toggle_btn,
838
  feedback_text,
839
  feedback_submit_btn,
 
967
  new_history[-1] = [last_user, last_assistant]
968
  answer = last_assistant
969
 
970
+ # LangSmith 日志
971
  student_id = user_id_val or "ANON"
972
  experiment_id = "RESP_AI_W10"
973
  try:
 
990
 
991
  new_status = render_session_status(mode_val, weaknesses, cognitive_state)
992
 
993
+ # 将当前这一轮的 Q/A 存入 state,后面 thumbs / 详细反馈用
994
  return "", new_history, weaknesses, cognitive_state, new_status, message, answer
995
 
996
  user_input.submit(
 
1137
 
1138
  # ===== Feedback Handlers =====
1139
  def show_feedback_box():
 
 
 
1140
  return {
1141
  feedback_text: gr.update(visible=True),
1142
  feedback_submit_btn: gr.update(visible=True),
 
1148
  [feedback_text, feedback_submit_btn],
1149
  )
1150
 
1151
+ def send_thumb_up(last_q, last_a, user_id_val, mode_val, model_name_val, lang_pref):
1152
+ if not last_q and not last_a:
1153
+ print("No last QA to log for thumbs_up.")
1154
+ return
1155
+ try:
1156
+ log_event(
1157
+ {
1158
+ "experiment_id": "RESP_AI_W10",
1159
+ "student_id": user_id_val or "ANON",
1160
+ "event_type": "thumbs_up",
1161
+ "timestamp": time.time(),
1162
+ "question": last_q,
1163
+ "answer": last_a,
1164
+ "model_name": model_name_val,
1165
+ "language": lang_pref,
1166
+ "learning_mode": mode_val,
1167
+ }
1168
+ )
1169
+ print("[Feedback] thumbs_up logged to LangSmith.")
1170
+ except Exception as e:
1171
+ print("thumb_up log error:", e)
1172
+
1173
+ def send_thumb_down(last_q, last_a, user_id_val, mode_val, model_name_val, lang_pref):
1174
+ if not last_q and not last_a:
1175
+ print("No last QA to log for thumbs_down.")
1176
+ return
1177
+ try:
1178
+ log_event(
1179
+ {
1180
+ "experiment_id": "RESP_AI_W10",
1181
+ "student_id": user_id_val or "ANON",
1182
+ "event_type": "thumbs_down",
1183
+ "timestamp": time.time(),
1184
+ "question": last_q,
1185
+ "answer": last_a,
1186
+ "model_name": model_name_val,
1187
+ "language": lang_pref,
1188
+ "learning_mode": mode_val,
1189
+ }
1190
+ )
1191
+ print("[Feedback] thumbs_down logged to LangSmith.")
1192
+ except Exception as e:
1193
+ print("thumb_down log error:", e)
1194
+
1195
+ thumb_up_btn.click(
1196
+ send_thumb_up,
1197
+ [
1198
+ last_question_state,
1199
+ last_answer_state,
1200
+ user_id_state,
1201
+ learning_mode,
1202
+ model_name,
1203
+ language_preference,
1204
+ ],
1205
+ [],
1206
+ )
1207
+
1208
+ thumb_down_btn.click(
1209
+ send_thumb_down,
1210
+ [
1211
+ last_question_state,
1212
+ last_answer_state,
1213
+ user_id_state,
1214
+ learning_mode,
1215
+ model_name,
1216
+ language_preference,
1217
+ ],
1218
+ [],
1219
+ )
1220
+
1221
  def submit_detailed_feedback(
1222
  text, last_q, last_a, user_id_val, mode_val, model_name_val, lang_pref
1223
  ):
1224
  if not text or not text.strip():
 
1225
  return gr.update(
1226
  value="",
1227
  placeholder="Please enter some feedback before submitting.",
 
1242
  "learning_mode": mode_val,
1243
  }
1244
  )
1245
+ print("[Feedback] detailed_feedback logged to LangSmith.")
1246
  except Exception as e:
1247
  print("detailed_feedback log error:", e)
1248
 
 
1249
  return gr.update(
1250
  value="",
1251
  placeholder="Thanks! Your feedback has been recorded.",
 
1265
  [feedback_text],
1266
  )
1267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1268
  # ===== Export / Summary =====
1269
  export_btn.click(
1270
  lambda h, c, m, w, cog: export_conversation(h, c, m, w, cog),