Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,6 +2,7 @@ import os
|
|
| 2 |
import time
|
| 3 |
import base64
|
| 4 |
import requests
|
|
|
|
| 5 |
from typing import List, Dict, Tuple, Optional
|
| 6 |
|
| 7 |
import gradio as gr
|
|
@@ -285,6 +286,50 @@ def log_event(data: Dict):
|
|
| 285 |
print("GSheet log exception:", e)
|
| 286 |
|
| 287 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
# ================== Gradio App ==================
|
| 289 |
with gr.Blocks(
|
| 290 |
title="Clare – Hanbridge AI Teaching Assistant", css=CUSTOM_CSS
|
|
@@ -299,6 +344,7 @@ with gr.Blocks(
|
|
| 299 |
# 用户状态(登录)
|
| 300 |
user_name_state = gr.State("")
|
| 301 |
user_id_state = gr.State("")
|
|
|
|
| 302 |
# --- Header ---
|
| 303 |
with gr.Row(elem_classes="header-container"):
|
| 304 |
with gr.Column(scale=3):
|
|
@@ -422,7 +468,6 @@ with gr.Blocks(
|
|
| 422 |
"""
|
| 423 |
)
|
| 424 |
|
| 425 |
-
|
| 426 |
# === Center Main ===
|
| 427 |
with gr.Column(scale=3):
|
| 428 |
|
|
@@ -504,7 +549,6 @@ with gr.Blocks(
|
|
| 504 |
)
|
| 505 |
session_status = gr.Markdown(visible=False)
|
| 506 |
|
| 507 |
-
|
| 508 |
# === Right Sidebar ===
|
| 509 |
with gr.Column(scale=1, min_width=180):
|
| 510 |
with gr.Group(elem_classes="login-panel"):
|
|
@@ -803,6 +847,16 @@ with gr.Blocks(
|
|
| 803 |
end_ts = time.time()
|
| 804 |
latency_ms = (end_ts - start_ts) * 1000.0
|
| 805 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 806 |
student_id = user_id_val or "ANON"
|
| 807 |
experiment_id = "RESP_AI_W10"
|
| 808 |
|
|
@@ -872,33 +926,32 @@ with gr.Blocks(
|
|
| 872 |
)
|
| 873 |
|
| 874 |
quiz_instruction = (
|
| 875 |
-
|
| 876 |
-
|
| 877 |
-
|
| 878 |
-
|
| 879 |
-
|
| 880 |
-
|
| 881 |
-
|
| 882 |
-
|
| 883 |
-
|
| 884 |
-
|
| 885 |
-
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
|
| 889 |
-
|
| 890 |
-
|
| 891 |
-
|
| 892 |
-
|
| 893 |
-
|
| 894 |
-
|
| 895 |
-
|
| 896 |
-
|
| 897 |
-
|
| 898 |
-
|
| 899 |
)
|
| 900 |
|
| 901 |
-
|
| 902 |
resolved_lang = lang_pref
|
| 903 |
|
| 904 |
start_ts = time.time()
|
|
|
|
| 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
|
|
|
|
| 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 |
+
期望 rag_engine 在每个 chunk 中填入:
|
| 299 |
+
chunk["source_file"] -> 文件名
|
| 300 |
+
chunk["section"] -> 段落 / section 描述(如 "Section 3.2 – Risk taxonomy")
|
| 301 |
+
若未提供,将回退为 "Unknown file" / "Related section"。
|
| 302 |
+
"""
|
| 303 |
+
if not rag_chunks:
|
| 304 |
+
return ""
|
| 305 |
+
|
| 306 |
+
refs_by_file: Dict[str, List[str]] = defaultdict(list)
|
| 307 |
+
|
| 308 |
+
for chunk in rag_chunks:
|
| 309 |
+
file_name = chunk.get("source_file") or "Unknown file"
|
| 310 |
+
section = chunk.get("section") or "Related section"
|
| 311 |
+
if section not in refs_by_file[file_name]:
|
| 312 |
+
refs_by_file[file_name].append(section)
|
| 313 |
+
|
| 314 |
+
if not refs_by_file:
|
| 315 |
+
return ""
|
| 316 |
+
|
| 317 |
+
lines = ["**References (RAG context used):**"]
|
| 318 |
+
for i, (file_name, sections) in enumerate(refs_by_file.items()):
|
| 319 |
+
if i >= max_files:
|
| 320 |
+
break
|
| 321 |
+
short_sections = sections[:max_sections_per_file]
|
| 322 |
+
if short_sections:
|
| 323 |
+
section_str = "; ".join(short_sections)
|
| 324 |
+
lines.append(f"- *{file_name}* — {section_str}")
|
| 325 |
+
else:
|
| 326 |
+
lines.append(f"- *{file_name}*")
|
| 327 |
+
|
| 328 |
+
if len(lines) == 1:
|
| 329 |
+
return ""
|
| 330 |
+
return "\n".join(lines)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
# ================== Gradio App ==================
|
| 334 |
with gr.Blocks(
|
| 335 |
title="Clare – Hanbridge AI Teaching Assistant", css=CUSTOM_CSS
|
|
|
|
| 344 |
# 用户状态(登录)
|
| 345 |
user_name_state = gr.State("")
|
| 346 |
user_id_state = gr.State("")
|
| 347 |
+
|
| 348 |
# --- Header ---
|
| 349 |
with gr.Row(elem_classes="header-container"):
|
| 350 |
with gr.Column(scale=3):
|
|
|
|
| 468 |
"""
|
| 469 |
)
|
| 470 |
|
|
|
|
| 471 |
# === Center Main ===
|
| 472 |
with gr.Column(scale=3):
|
| 473 |
|
|
|
|
| 549 |
)
|
| 550 |
session_status = gr.Markdown(visible=False)
|
| 551 |
|
|
|
|
| 552 |
# === Right Sidebar ===
|
| 553 |
with gr.Column(scale=1, min_width=180):
|
| 554 |
with gr.Group(elem_classes="login-panel"):
|
|
|
|
| 847 |
end_ts = time.time()
|
| 848 |
latency_ms = (end_ts - start_ts) * 1000.0
|
| 849 |
|
| 850 |
+
# === 在这里附上 References ===
|
| 851 |
+
ref_text = format_references(rag_context)
|
| 852 |
+
if ref_text and new_history:
|
| 853 |
+
last_user, last_assistant = new_history[-1]
|
| 854 |
+
if "References (RAG context used):" not in (last_assistant or ""):
|
| 855 |
+
last_assistant = f"{last_assistant}\n\n{ref_text}"
|
| 856 |
+
new_history[-1] = [last_user, last_assistant]
|
| 857 |
+
answer = last_assistant # 同步给日志
|
| 858 |
+
# ==============================
|
| 859 |
+
|
| 860 |
student_id = user_id_val or "ANON"
|
| 861 |
experiment_id = "RESP_AI_W10"
|
| 862 |
|
|
|
|
| 926 |
)
|
| 927 |
|
| 928 |
quiz_instruction = (
|
| 929 |
+
"We are running a short micro-quiz session based ONLY on **Module 10 – "
|
| 930 |
+
"Responsible AI (Alto, 2024, Chapter 12)** and the pre-loaded materials.\n\n"
|
| 931 |
+
"Step 1 – Before asking any content question:\n"
|
| 932 |
+
"• First ask me which quiz style I prefer right now:\n"
|
| 933 |
+
" - (1) Multiple-choice questions\n"
|
| 934 |
+
" - (2) Short-answer / open-ended questions\n"
|
| 935 |
+
"• Ask me explicitly: \"Which quiz style do you prefer now: 1) Multiple-choice or 2) Short-answer? "
|
| 936 |
+
"Please reply with 1 or 2.\"\n"
|
| 937 |
+
"• Do NOT start a content question until I have answered 1 or 2.\n\n"
|
| 938 |
+
"Step 2 – After I choose the style:\n"
|
| 939 |
+
"• If I choose 1 (multiple-choice):\n"
|
| 940 |
+
" - Ask ONE multiple-choice question at a time, based on Module 10 concepts "
|
| 941 |
+
"(Responsible AI definition, risk types, mitigation layers, EU AI Act, etc.).\n"
|
| 942 |
+
" - Provide 3–4 options (A, B, C, D) and make only one option clearly correct.\n"
|
| 943 |
+
"• If I choose 2 (short-answer):\n"
|
| 944 |
+
" - Ask ONE short-answer question at a time, also based on Module 10 concepts.\n"
|
| 945 |
+
" - Do NOT show the answer when you ask the question.\n\n"
|
| 946 |
+
"Step 3 – For each answer I give:\n"
|
| 947 |
+
"• Grade my answer (correct / partially correct / incorrect).\n"
|
| 948 |
+
"• Give a brief explanation and the correct answer.\n"
|
| 949 |
+
"• Then ask if I want another question of the SAME style.\n"
|
| 950 |
+
"• Continue this pattern until I explicitly say to stop.\n\n"
|
| 951 |
+
"Please start by asking me which quiz style I prefer (1 = multiple-choice, 2 = short-answer). "
|
| 952 |
+
"Do not ask any content question before I choose."
|
| 953 |
)
|
| 954 |
|
|
|
|
| 955 |
resolved_lang = lang_pref
|
| 956 |
|
| 957 |
start_ts = time.time()
|