Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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
|
| 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 |
-
# =====
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
|
| 270 |
def log_event(data: Dict):
|
| 271 |
"""
|
| 272 |
-
|
|
|
|
| 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 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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",
|
| 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),
|