Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -50,6 +50,7 @@ def image_to_base64(image_path: str) -> str:
|
|
| 50 |
mime = "image/png"
|
| 51 |
return f"data:{mime};base64,{encoded_string}"
|
| 52 |
|
|
|
|
| 53 |
# ================== User Guide Content ==================
|
| 54 |
USER_GUIDE_SECTIONS = {
|
| 55 |
"getting_started": """
|
|
@@ -389,6 +390,9 @@ with gr.Blocks(
|
|
| 389 |
# ✅ 当前“最近一次回答”是否已经被点赞/点踩(只允许一次)
|
| 390 |
feedback_used_state = gr.State(False)
|
| 391 |
|
|
|
|
|
|
|
|
|
|
| 392 |
# --- Header ---
|
| 393 |
with gr.Row(elem_classes="header-container"):
|
| 394 |
with gr.Column(scale=3):
|
|
@@ -532,6 +536,9 @@ with gr.Blocks(
|
|
| 532 |
type="tuples",
|
| 533 |
)
|
| 534 |
|
|
|
|
|
|
|
|
|
|
| 535 |
# Rating bar (last answer)
|
| 536 |
gr.Markdown("#### Rate Clare’s last answer")
|
| 537 |
with gr.Row():
|
|
@@ -701,6 +708,8 @@ with gr.Blocks(
|
|
| 701 |
feedback_toggle_btn: gr.update(interactive=False),
|
| 702 |
feedback_text: gr.update(visible=False, value=""),
|
| 703 |
feedback_submit_btn: gr.update(interactive=False, visible=False),
|
|
|
|
|
|
|
| 704 |
}
|
| 705 |
|
| 706 |
info_html = f"""
|
|
@@ -738,6 +747,8 @@ with gr.Blocks(
|
|
| 738 |
feedback_toggle_btn: gr.update(interactive=True),
|
| 739 |
feedback_text: gr.update(visible=False, value=""),
|
| 740 |
feedback_submit_btn: gr.update(interactive=True, visible=False),
|
|
|
|
|
|
|
| 741 |
}
|
| 742 |
|
| 743 |
login_confirm_btn.click(
|
|
@@ -768,6 +779,8 @@ with gr.Blocks(
|
|
| 768 |
feedback_toggle_btn,
|
| 769 |
feedback_text,
|
| 770 |
feedback_submit_btn,
|
|
|
|
|
|
|
| 771 |
],
|
| 772 |
)
|
| 773 |
|
|
@@ -802,6 +815,8 @@ with gr.Blocks(
|
|
| 802 |
feedback_toggle_btn: gr.update(interactive=False),
|
| 803 |
feedback_text: gr.update(visible=False, value=""),
|
| 804 |
feedback_submit_btn: gr.update(interactive=False, visible=False),
|
|
|
|
|
|
|
| 805 |
}
|
| 806 |
|
| 807 |
logout_btn.click(
|
|
@@ -832,6 +847,8 @@ with gr.Blocks(
|
|
| 832 |
feedback_toggle_btn,
|
| 833 |
feedback_text,
|
| 834 |
feedback_submit_btn,
|
|
|
|
|
|
|
| 835 |
],
|
| 836 |
)
|
| 837 |
|
|
@@ -895,6 +912,14 @@ with gr.Blocks(
|
|
| 895 |
user_id_val,
|
| 896 |
feedback_used,
|
| 897 |
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 898 |
# 未登录:不解锁按钮
|
| 899 |
if not user_id_val:
|
| 900 |
out_msg = (
|
|
@@ -907,6 +932,7 @@ with gr.Blocks(
|
|
| 907 |
weaknesses or [],
|
| 908 |
cognitive_state or {"confusion": 0, "mastery": 0},
|
| 909 |
)
|
|
|
|
| 910 |
return (
|
| 911 |
"",
|
| 912 |
new_history,
|
|
@@ -918,9 +944,14 @@ with gr.Blocks(
|
|
| 918 |
feedback_used,
|
| 919 |
gr.update(interactive=False, value="👍 Helpful"),
|
| 920 |
gr.update(interactive=False, value="👎 Not helpful"),
|
|
|
|
|
|
|
| 921 |
)
|
| 922 |
|
|
|
|
| 923 |
resolved_lang = detect_language(message or "", lang_pref)
|
|
|
|
|
|
|
| 924 |
|
| 925 |
# 空输入:不改变按钮状态
|
| 926 |
if not message or not message.strip():
|
|
@@ -929,6 +960,7 @@ with gr.Blocks(
|
|
| 929 |
weaknesses or [],
|
| 930 |
cognitive_state or {"confusion": 0, "mastery": 0},
|
| 931 |
)
|
|
|
|
| 932 |
return (
|
| 933 |
"",
|
| 934 |
chat_history,
|
|
@@ -940,20 +972,35 @@ with gr.Blocks(
|
|
| 940 |
feedback_used,
|
| 941 |
gr.update(),
|
| 942 |
gr.update(),
|
|
|
|
|
|
|
| 943 |
)
|
| 944 |
|
|
|
|
|
|
|
| 945 |
weaknesses = update_weaknesses_from_message(message, weaknesses or [])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 946 |
cognitive_state = update_cognitive_state_from_message(message, cognitive_state)
|
|
|
|
|
|
|
| 947 |
|
|
|
|
|
|
|
| 948 |
if is_academic_query(message):
|
| 949 |
rag_context_text, rag_used_chunks = retrieve_relevant_chunks(
|
| 950 |
message, rag_chunks or []
|
| 951 |
)
|
| 952 |
else:
|
| 953 |
rag_context_text, rag_used_chunks = "", []
|
|
|
|
|
|
|
| 954 |
|
| 955 |
-
|
| 956 |
-
|
|
|
|
| 957 |
message=message,
|
| 958 |
history=chat_history,
|
| 959 |
model_name=model_name_val,
|
|
@@ -965,9 +1012,31 @@ with gr.Blocks(
|
|
| 965 |
cognitive_state=cognitive_state,
|
| 966 |
rag_context=rag_context_text,
|
| 967 |
)
|
| 968 |
-
|
| 969 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 970 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 971 |
if is_academic_query(message) and rag_used_chunks:
|
| 972 |
ref_text = format_references(rag_used_chunks)
|
| 973 |
else:
|
|
@@ -980,6 +1049,7 @@ with gr.Blocks(
|
|
| 980 |
new_history[-1] = [last_user, last_assistant]
|
| 981 |
answer = last_assistant
|
| 982 |
|
|
|
|
| 983 |
student_id = user_id_val or "ANON"
|
| 984 |
experiment_id = "RESP_AI_W10"
|
| 985 |
try:
|
|
@@ -988,13 +1058,18 @@ with gr.Blocks(
|
|
| 988 |
"experiment_id": experiment_id,
|
| 989 |
"student_id": student_id,
|
| 990 |
"event_type": "chat_turn",
|
| 991 |
-
"timestamp":
|
| 992 |
-
"latency_ms":
|
| 993 |
"question": message,
|
| 994 |
"answer": answer,
|
| 995 |
"model_name": model_name_val,
|
| 996 |
"language": resolved_lang,
|
| 997 |
"learning_mode": mode_val,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 998 |
}
|
| 999 |
)
|
| 1000 |
except Exception as e:
|
|
@@ -1014,6 +1089,8 @@ with gr.Blocks(
|
|
| 1014 |
False,
|
| 1015 |
gr.update(interactive=True, value="👍 Helpful"),
|
| 1016 |
gr.update(interactive=True, value="👎 Not helpful"),
|
|
|
|
|
|
|
| 1017 |
)
|
| 1018 |
|
| 1019 |
user_input.submit(
|
|
@@ -1043,6 +1120,8 @@ with gr.Blocks(
|
|
| 1043 |
feedback_used_state,
|
| 1044 |
thumb_up_btn,
|
| 1045 |
thumb_down_btn,
|
|
|
|
|
|
|
| 1046 |
],
|
| 1047 |
)
|
| 1048 |
|
|
@@ -1059,8 +1138,16 @@ with gr.Blocks(
|
|
| 1059 |
doc_type_val,
|
| 1060 |
user_id_val,
|
| 1061 |
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1062 |
if not user_id_val:
|
| 1063 |
gr.Info("Please log in first to start a micro-quiz.", title="Login required")
|
|
|
|
| 1064 |
return (
|
| 1065 |
chat_history,
|
| 1066 |
weaknesses,
|
|
@@ -1070,6 +1157,8 @@ with gr.Blocks(
|
|
| 1070 |
weaknesses or [],
|
| 1071 |
cognitive_state or {"confusion": 0, "mastery": 0},
|
| 1072 |
),
|
|
|
|
|
|
|
| 1073 |
)
|
| 1074 |
|
| 1075 |
quiz_instruction = (
|
|
@@ -1100,13 +1189,18 @@ with gr.Blocks(
|
|
| 1100 |
)
|
| 1101 |
|
| 1102 |
resolved_lang = lang_pref
|
|
|
|
|
|
|
| 1103 |
|
| 1104 |
-
|
| 1105 |
quiz_ctx_text, _quiz_ctx_chunks = retrieve_relevant_chunks(
|
| 1106 |
"Module 10 quiz", rag_chunks or []
|
| 1107 |
)
|
|
|
|
|
|
|
| 1108 |
|
| 1109 |
-
|
|
|
|
| 1110 |
message=quiz_instruction,
|
| 1111 |
history=chat_history,
|
| 1112 |
model_name=model_name_val,
|
|
@@ -1118,32 +1212,49 @@ with gr.Blocks(
|
|
| 1118 |
cognitive_state=cognitive_state,
|
| 1119 |
rag_context=quiz_ctx_text,
|
| 1120 |
)
|
| 1121 |
-
|
| 1122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1123 |
|
| 1124 |
student_id = user_id_val or "ANON"
|
| 1125 |
experiment_id = "RESP_AI_W10"
|
| 1126 |
-
|
| 1127 |
try:
|
| 1128 |
log_event(
|
| 1129 |
{
|
| 1130 |
"experiment_id": experiment_id,
|
| 1131 |
"student_id": student_id,
|
| 1132 |
"event_type": "micro_quiz_start",
|
| 1133 |
-
"timestamp":
|
| 1134 |
-
"latency_ms":
|
| 1135 |
"question": quiz_instruction,
|
| 1136 |
"answer": answer,
|
| 1137 |
"model_name": model_name_val,
|
| 1138 |
"language": resolved_lang,
|
| 1139 |
"learning_mode": mode_val,
|
|
|
|
| 1140 |
}
|
| 1141 |
)
|
| 1142 |
except Exception as e:
|
| 1143 |
print("log_event error:", e)
|
| 1144 |
|
| 1145 |
new_status = render_session_status(mode_val, weaknesses, cognitive_state)
|
| 1146 |
-
return new_history, weaknesses, cognitive_state, new_status
|
| 1147 |
|
| 1148 |
quiz_btn.click(
|
| 1149 |
start_micro_quiz,
|
|
@@ -1159,7 +1270,7 @@ with gr.Blocks(
|
|
| 1159 |
doc_type,
|
| 1160 |
user_id_state,
|
| 1161 |
],
|
| 1162 |
-
[chatbot, weakness_state, cognitive_state_state, session_status],
|
| 1163 |
)
|
| 1164 |
|
| 1165 |
# ===== Feedback Handlers (thumb + detailed) =====
|
|
@@ -1389,6 +1500,8 @@ with gr.Blocks(
|
|
| 1389 |
False,
|
| 1390 |
gr.update(interactive=False, value="👍 Helpful"),
|
| 1391 |
gr.update(interactive=False, value="👎 Not helpful"),
|
|
|
|
|
|
|
| 1392 |
)
|
| 1393 |
|
| 1394 |
clear_btn.click(
|
|
@@ -1406,6 +1519,8 @@ with gr.Blocks(
|
|
| 1406 |
feedback_used_state,
|
| 1407 |
thumb_up_btn,
|
| 1408 |
thumb_down_btn,
|
|
|
|
|
|
|
| 1409 |
],
|
| 1410 |
queue=False,
|
| 1411 |
)
|
|
|
|
| 50 |
mime = "image/png"
|
| 51 |
return f"data:{mime};base64,{encoded_string}"
|
| 52 |
|
| 53 |
+
|
| 54 |
# ================== User Guide Content ==================
|
| 55 |
USER_GUIDE_SECTIONS = {
|
| 56 |
"getting_started": """
|
|
|
|
| 390 |
# ✅ 当前“最近一次回答”是否已经被点赞/点踩(只允许一次)
|
| 391 |
feedback_used_state = gr.State(False)
|
| 392 |
|
| 393 |
+
# ✅ 性能输出
|
| 394 |
+
perf_state = gr.State({})
|
| 395 |
+
|
| 396 |
# --- Header ---
|
| 397 |
with gr.Row(elem_classes="header-container"):
|
| 398 |
with gr.Column(scale=3):
|
|
|
|
| 536 |
type="tuples",
|
| 537 |
)
|
| 538 |
|
| 539 |
+
# ✅ profiling output JSON (shows your TTFT / tokens/sec etc.)
|
| 540 |
+
perf_output = gr.JSON(label="Output", value={})
|
| 541 |
+
|
| 542 |
# Rating bar (last answer)
|
| 543 |
gr.Markdown("#### Rate Clare’s last answer")
|
| 544 |
with gr.Row():
|
|
|
|
| 708 |
feedback_toggle_btn: gr.update(interactive=False),
|
| 709 |
feedback_text: gr.update(visible=False, value=""),
|
| 710 |
feedback_submit_btn: gr.update(interactive=False, visible=False),
|
| 711 |
+
perf_state: {}, # ✅
|
| 712 |
+
perf_output: gr.update(value={}), # ✅
|
| 713 |
}
|
| 714 |
|
| 715 |
info_html = f"""
|
|
|
|
| 747 |
feedback_toggle_btn: gr.update(interactive=True),
|
| 748 |
feedback_text: gr.update(visible=False, value=""),
|
| 749 |
feedback_submit_btn: gr.update(interactive=True, visible=False),
|
| 750 |
+
perf_state: {}, # ✅
|
| 751 |
+
perf_output: gr.update(value={}), # ✅
|
| 752 |
}
|
| 753 |
|
| 754 |
login_confirm_btn.click(
|
|
|
|
| 779 |
feedback_toggle_btn,
|
| 780 |
feedback_text,
|
| 781 |
feedback_submit_btn,
|
| 782 |
+
perf_state, # ✅
|
| 783 |
+
perf_output, # ✅
|
| 784 |
],
|
| 785 |
)
|
| 786 |
|
|
|
|
| 815 |
feedback_toggle_btn: gr.update(interactive=False),
|
| 816 |
feedback_text: gr.update(visible=False, value=""),
|
| 817 |
feedback_submit_btn: gr.update(interactive=False, visible=False),
|
| 818 |
+
perf_state: {}, # ✅
|
| 819 |
+
perf_output: gr.update(value={}), # ✅
|
| 820 |
}
|
| 821 |
|
| 822 |
logout_btn.click(
|
|
|
|
| 847 |
feedback_toggle_btn,
|
| 848 |
feedback_text,
|
| 849 |
feedback_submit_btn,
|
| 850 |
+
perf_state, # ✅
|
| 851 |
+
perf_output, # ✅
|
| 852 |
],
|
| 853 |
)
|
| 854 |
|
|
|
|
| 912 |
user_id_val,
|
| 913 |
feedback_used,
|
| 914 |
):
|
| 915 |
+
# perf container
|
| 916 |
+
t0 = time.perf_counter()
|
| 917 |
+
marks = {"start": 0}
|
| 918 |
+
segs = {}
|
| 919 |
+
|
| 920 |
+
def mark(name: str):
|
| 921 |
+
marks[name] = (time.perf_counter() - t0) * 1000.0
|
| 922 |
+
|
| 923 |
# 未登录:不解锁按钮
|
| 924 |
if not user_id_val:
|
| 925 |
out_msg = (
|
|
|
|
| 932 |
weaknesses or [],
|
| 933 |
cognitive_state or {"confusion": 0, "mastery": 0},
|
| 934 |
)
|
| 935 |
+
perf = {"marks_ms": marks, "segments_ms": segs, "total_ms": (time.perf_counter() - t0) * 1000.0}
|
| 936 |
return (
|
| 937 |
"",
|
| 938 |
new_history,
|
|
|
|
| 944 |
feedback_used,
|
| 945 |
gr.update(interactive=False, value="👍 Helpful"),
|
| 946 |
gr.update(interactive=False, value="👎 Not helpful"),
|
| 947 |
+
perf, # ✅ perf_state
|
| 948 |
+
perf, # ✅ perf_output
|
| 949 |
)
|
| 950 |
|
| 951 |
+
# language detect
|
| 952 |
resolved_lang = detect_language(message or "", lang_pref)
|
| 953 |
+
mark("language_detect_done")
|
| 954 |
+
segs["language_detect_done"] = marks["language_detect_done"] - marks["start"]
|
| 955 |
|
| 956 |
# 空输入:不改变按钮状态
|
| 957 |
if not message or not message.strip():
|
|
|
|
| 960 |
weaknesses or [],
|
| 961 |
cognitive_state or {"confusion": 0, "mastery": 0},
|
| 962 |
)
|
| 963 |
+
perf = {"marks_ms": marks, "segments_ms": segs, "total_ms": (time.perf_counter() - t0) * 1000.0}
|
| 964 |
return (
|
| 965 |
"",
|
| 966 |
chat_history,
|
|
|
|
| 972 |
feedback_used,
|
| 973 |
gr.update(),
|
| 974 |
gr.update(),
|
| 975 |
+
perf, # ✅
|
| 976 |
+
perf, # ✅
|
| 977 |
)
|
| 978 |
|
| 979 |
+
# weakness/cognitive updates
|
| 980 |
+
t_w0 = time.perf_counter()
|
| 981 |
weaknesses = update_weaknesses_from_message(message, weaknesses or [])
|
| 982 |
+
mark("weakness_update_done")
|
| 983 |
+
segs["weakness_update_done"] = (time.perf_counter() - t_w0) * 1000.0
|
| 984 |
+
|
| 985 |
+
t_c0 = time.perf_counter()
|
| 986 |
cognitive_state = update_cognitive_state_from_message(message, cognitive_state)
|
| 987 |
+
mark("cognitive_update_done")
|
| 988 |
+
segs["cognitive_update_done"] = (time.perf_counter() - t_c0) * 1000.0
|
| 989 |
|
| 990 |
+
# RAG
|
| 991 |
+
t_r0 = time.perf_counter()
|
| 992 |
if is_academic_query(message):
|
| 993 |
rag_context_text, rag_used_chunks = retrieve_relevant_chunks(
|
| 994 |
message, rag_chunks or []
|
| 995 |
)
|
| 996 |
else:
|
| 997 |
rag_context_text, rag_used_chunks = "", []
|
| 998 |
+
mark("rag_retrieve_done")
|
| 999 |
+
segs["rag_retrieve_done"] = (time.perf_counter() - t_r0) * 1000.0
|
| 1000 |
|
| 1001 |
+
# LLM (chat_with_clare must return 3 values)
|
| 1002 |
+
t_llm0 = time.perf_counter()
|
| 1003 |
+
answer, new_history, llm_stats = chat_with_clare(
|
| 1004 |
message=message,
|
| 1005 |
history=chat_history,
|
| 1006 |
model_name=model_name_val,
|
|
|
|
| 1012 |
cognitive_state=cognitive_state,
|
| 1013 |
rag_context=rag_context_text,
|
| 1014 |
)
|
| 1015 |
+
mark("llm_done")
|
| 1016 |
+
segs["llm_done"] = (time.perf_counter() - t_llm0) * 1000.0
|
| 1017 |
+
|
| 1018 |
+
# merge llm_stats into perf
|
| 1019 |
+
perf = {
|
| 1020 |
+
"marks_ms": marks,
|
| 1021 |
+
"segments_ms": segs,
|
| 1022 |
+
"total_ms": marks.get("llm_done", (time.perf_counter() - t0) * 1000.0),
|
| 1023 |
+
}
|
| 1024 |
|
| 1025 |
+
# llm_stats may contain marks_ms/segments_ms/llm_profile
|
| 1026 |
+
if isinstance(llm_stats, dict):
|
| 1027 |
+
if "llm_profile" in llm_stats:
|
| 1028 |
+
perf.update({"llm_profile": llm_stats.get("llm_profile", {})})
|
| 1029 |
+
# merge marks/segments from llm_stats if present
|
| 1030 |
+
ms2 = llm_stats.get("marks_ms") if isinstance(llm_stats.get("marks_ms"), dict) else {}
|
| 1031 |
+
sg2 = llm_stats.get("segments_ms") if isinstance(llm_stats.get("segments_ms"), dict) else {}
|
| 1032 |
+
for k, v in ms2.items():
|
| 1033 |
+
if v is not None:
|
| 1034 |
+
perf["marks_ms"][k] = v
|
| 1035 |
+
for k, v in sg2.items():
|
| 1036 |
+
if v is not None:
|
| 1037 |
+
perf["segments_ms"][k] = v
|
| 1038 |
+
|
| 1039 |
+
# References formatting
|
| 1040 |
if is_academic_query(message) and rag_used_chunks:
|
| 1041 |
ref_text = format_references(rag_used_chunks)
|
| 1042 |
else:
|
|
|
|
| 1049 |
new_history[-1] = [last_user, last_assistant]
|
| 1050 |
answer = last_assistant
|
| 1051 |
|
| 1052 |
+
# LangSmith event
|
| 1053 |
student_id = user_id_val or "ANON"
|
| 1054 |
experiment_id = "RESP_AI_W10"
|
| 1055 |
try:
|
|
|
|
| 1058 |
"experiment_id": experiment_id,
|
| 1059 |
"student_id": student_id,
|
| 1060 |
"event_type": "chat_turn",
|
| 1061 |
+
"timestamp": time.time(),
|
| 1062 |
+
"latency_ms": perf["total_ms"],
|
| 1063 |
"question": message,
|
| 1064 |
"answer": answer,
|
| 1065 |
"model_name": model_name_val,
|
| 1066 |
"language": resolved_lang,
|
| 1067 |
"learning_mode": mode_val,
|
| 1068 |
+
# ✅ extra profiling fields if available
|
| 1069 |
+
"ttft_ms": perf.get("segments_ms", {}).get("llm_ttft_ms"),
|
| 1070 |
+
"tokens_per_sec": (perf.get("llm_profile", {}) or {}).get("tokens_per_sec_est"),
|
| 1071 |
+
"output_tokens_est": (perf.get("llm_profile", {}) or {}).get("output_tokens_est"),
|
| 1072 |
+
"streaming_used": (perf.get("llm_profile", {}) or {}).get("streaming_used"),
|
| 1073 |
}
|
| 1074 |
)
|
| 1075 |
except Exception as e:
|
|
|
|
| 1089 |
False,
|
| 1090 |
gr.update(interactive=True, value="👍 Helpful"),
|
| 1091 |
gr.update(interactive=True, value="👎 Not helpful"),
|
| 1092 |
+
perf, # ✅ perf_state
|
| 1093 |
+
perf, # ✅ perf_output
|
| 1094 |
)
|
| 1095 |
|
| 1096 |
user_input.submit(
|
|
|
|
| 1120 |
feedback_used_state,
|
| 1121 |
thumb_up_btn,
|
| 1122 |
thumb_down_btn,
|
| 1123 |
+
perf_state, # ✅
|
| 1124 |
+
perf_output, # ✅
|
| 1125 |
],
|
| 1126 |
)
|
| 1127 |
|
|
|
|
| 1138 |
doc_type_val,
|
| 1139 |
user_id_val,
|
| 1140 |
):
|
| 1141 |
+
t0 = time.perf_counter()
|
| 1142 |
+
marks = {"start": 0}
|
| 1143 |
+
segs = {}
|
| 1144 |
+
|
| 1145 |
+
def mark(name: str):
|
| 1146 |
+
marks[name] = (time.perf_counter() - t0) * 1000.0
|
| 1147 |
+
|
| 1148 |
if not user_id_val:
|
| 1149 |
gr.Info("Please log in first to start a micro-quiz.", title="Login required")
|
| 1150 |
+
perf = {"marks_ms": marks, "segments_ms": segs, "total_ms": (time.perf_counter() - t0) * 1000.0}
|
| 1151 |
return (
|
| 1152 |
chat_history,
|
| 1153 |
weaknesses,
|
|
|
|
| 1157 |
weaknesses or [],
|
| 1158 |
cognitive_state or {"confusion": 0, "mastery": 0},
|
| 1159 |
),
|
| 1160 |
+
perf,
|
| 1161 |
+
perf,
|
| 1162 |
)
|
| 1163 |
|
| 1164 |
quiz_instruction = (
|
|
|
|
| 1189 |
)
|
| 1190 |
|
| 1191 |
resolved_lang = lang_pref
|
| 1192 |
+
mark("language_detect_done")
|
| 1193 |
+
segs["language_detect_done"] = marks["language_detect_done"]
|
| 1194 |
|
| 1195 |
+
t_r0 = time.perf_counter()
|
| 1196 |
quiz_ctx_text, _quiz_ctx_chunks = retrieve_relevant_chunks(
|
| 1197 |
"Module 10 quiz", rag_chunks or []
|
| 1198 |
)
|
| 1199 |
+
mark("rag_retrieve_done")
|
| 1200 |
+
segs["rag_retrieve_done"] = (time.perf_counter() - t_r0) * 1000.0
|
| 1201 |
|
| 1202 |
+
t_llm0 = time.perf_counter()
|
| 1203 |
+
answer, new_history, llm_stats = chat_with_clare(
|
| 1204 |
message=quiz_instruction,
|
| 1205 |
history=chat_history,
|
| 1206 |
model_name=model_name_val,
|
|
|
|
| 1212 |
cognitive_state=cognitive_state,
|
| 1213 |
rag_context=quiz_ctx_text,
|
| 1214 |
)
|
| 1215 |
+
mark("llm_done")
|
| 1216 |
+
segs["llm_done"] = (time.perf_counter() - t_llm0) * 1000.0
|
| 1217 |
+
|
| 1218 |
+
perf = {
|
| 1219 |
+
"marks_ms": marks,
|
| 1220 |
+
"segments_ms": segs,
|
| 1221 |
+
"total_ms": marks.get("llm_done", (time.perf_counter() - t0) * 1000.0),
|
| 1222 |
+
}
|
| 1223 |
+
if isinstance(llm_stats, dict):
|
| 1224 |
+
if "llm_profile" in llm_stats:
|
| 1225 |
+
perf.update({"llm_profile": llm_stats.get("llm_profile", {})})
|
| 1226 |
+
ms2 = llm_stats.get("marks_ms") if isinstance(llm_stats.get("marks_ms"), dict) else {}
|
| 1227 |
+
sg2 = llm_stats.get("segments_ms") if isinstance(llm_stats.get("segments_ms"), dict) else {}
|
| 1228 |
+
for k, v in ms2.items():
|
| 1229 |
+
if v is not None:
|
| 1230 |
+
perf["marks_ms"][k] = v
|
| 1231 |
+
for k, v in sg2.items():
|
| 1232 |
+
if v is not None:
|
| 1233 |
+
perf["segments_ms"][k] = v
|
| 1234 |
|
| 1235 |
student_id = user_id_val or "ANON"
|
| 1236 |
experiment_id = "RESP_AI_W10"
|
|
|
|
| 1237 |
try:
|
| 1238 |
log_event(
|
| 1239 |
{
|
| 1240 |
"experiment_id": experiment_id,
|
| 1241 |
"student_id": student_id,
|
| 1242 |
"event_type": "micro_quiz_start",
|
| 1243 |
+
"timestamp": time.time(),
|
| 1244 |
+
"latency_ms": perf["total_ms"],
|
| 1245 |
"question": quiz_instruction,
|
| 1246 |
"answer": answer,
|
| 1247 |
"model_name": model_name_val,
|
| 1248 |
"language": resolved_lang,
|
| 1249 |
"learning_mode": mode_val,
|
| 1250 |
+
"ttft_ms": perf.get("segments_ms", {}).get("llm_ttft_ms"),
|
| 1251 |
}
|
| 1252 |
)
|
| 1253 |
except Exception as e:
|
| 1254 |
print("log_event error:", e)
|
| 1255 |
|
| 1256 |
new_status = render_session_status(mode_val, weaknesses, cognitive_state)
|
| 1257 |
+
return new_history, weaknesses, cognitive_state, new_status, perf, perf
|
| 1258 |
|
| 1259 |
quiz_btn.click(
|
| 1260 |
start_micro_quiz,
|
|
|
|
| 1270 |
doc_type,
|
| 1271 |
user_id_state,
|
| 1272 |
],
|
| 1273 |
+
[chatbot, weakness_state, cognitive_state_state, session_status, perf_state, perf_output],
|
| 1274 |
)
|
| 1275 |
|
| 1276 |
# ===== Feedback Handlers (thumb + detailed) =====
|
|
|
|
| 1500 |
False,
|
| 1501 |
gr.update(interactive=False, value="👍 Helpful"),
|
| 1502 |
gr.update(interactive=False, value="👎 Not helpful"),
|
| 1503 |
+
{},
|
| 1504 |
+
{},
|
| 1505 |
)
|
| 1506 |
|
| 1507 |
clear_btn.click(
|
|
|
|
| 1519 |
feedback_used_state,
|
| 1520 |
thumb_up_btn,
|
| 1521 |
thumb_down_btn,
|
| 1522 |
+
perf_state,
|
| 1523 |
+
perf_output,
|
| 1524 |
],
|
| 1525 |
queue=False,
|
| 1526 |
)
|