Spaces:
Sleeping
Sleeping
update
Browse files
app.py
CHANGED
|
@@ -65,6 +65,8 @@ OPEN_AI_CLIENT = OpenAI(api_key=OPEN_AI_KEY)
|
|
| 65 |
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
| 66 |
GROQ_CLIENT = Groq(api_key=GROQ_API_KEY)
|
| 67 |
|
|
|
|
|
|
|
| 68 |
DRIVE_KEY = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON")
|
| 69 |
GCS_KEY = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON")
|
| 70 |
|
|
@@ -475,9 +477,23 @@ def process_transcript_and_screenshots_on_gcs(video_id):
|
|
| 475 |
# get_mind_map(video_id, transcript_text, source)
|
| 476 |
# print("===確認其他衍生文件 end ===")
|
| 477 |
|
|
|
|
| 478 |
# 處理截圖
|
| 479 |
for entry in transcript:
|
| 480 |
if 'img_file_id' not in entry:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
screenshot_path = screenshot_youtube_video(video_id, entry['start'])
|
| 482 |
screenshot_blob_name = f"{video_id}/{video_id}_{entry['start']}.jpg"
|
| 483 |
img_file_id = upload_img_and_get_public_url(gcs_client, bucket_name, screenshot_blob_name, screenshot_path)
|
|
@@ -503,7 +519,6 @@ def process_youtube_link(password, link):
|
|
| 503 |
video_id = extract_youtube_id(link)
|
| 504 |
global VIDEO_ID
|
| 505 |
VIDEO_ID = video_id
|
| 506 |
-
download_youtube_video(video_id, output_path=OUTPUT_PATH)
|
| 507 |
|
| 508 |
try:
|
| 509 |
# transcript = process_transcript_and_screenshots(video_id)
|
|
@@ -560,6 +575,8 @@ def process_youtube_link(password, link):
|
|
| 560 |
mind_map_json = get_mind_map(video_id, formatted_simple_transcript, source)
|
| 561 |
mind_map = mind_map_json["mind_map"]
|
| 562 |
mind_map_html = get_mind_map_html(mind_map)
|
|
|
|
|
|
|
| 563 |
|
| 564 |
# 确保返回与 UI 组件预期匹配的输出
|
| 565 |
return video_id, \
|
|
@@ -573,7 +590,8 @@ def process_youtube_link(password, link):
|
|
| 573 |
html_content, \
|
| 574 |
simple_html_content, \
|
| 575 |
first_image, \
|
| 576 |
-
first_text,
|
|
|
|
| 577 |
|
| 578 |
def format_transcript_to_html(formatted_transcript):
|
| 579 |
html_content = ""
|
|
@@ -629,6 +647,78 @@ def process_web_link(link):
|
|
| 629 |
|
| 630 |
|
| 631 |
# ---- LLM Generator ----
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 632 |
def get_mind_map(video_id, df_string, source):
|
| 633 |
if source == "gcs":
|
| 634 |
print("===get_mind_map on gcs===")
|
|
@@ -910,7 +1000,7 @@ def change_questions(password, df_string):
|
|
| 910 |
return q1, q2, q3
|
| 911 |
|
| 912 |
# --- 學習單 ---
|
| 913 |
-
def generate_worksheet(password, df_string):
|
| 914 |
verify_password(password)
|
| 915 |
|
| 916 |
# df_string delete embed_url, screenshot_path
|
|
@@ -920,15 +1010,21 @@ def generate_worksheet(password, df_string):
|
|
| 920 |
entry.pop('screenshot_path', None)
|
| 921 |
df_string_text = json.dumps(df_string_json, ensure_ascii=False, indent=2)
|
| 922 |
|
| 923 |
-
worksheet_prompt = get_worksheet_prompt()
|
| 924 |
|
| 925 |
# 使用 OpenAI 生成基于上传数据的问题
|
| 926 |
sys_content = "你是一個擅長資料分析跟影片教學備課的老師,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
|
| 927 |
user_content = f"""
|
| 928 |
這是逐字稿:{df_string_text}
|
| 929 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 930 |
請根據逐字稿進行以下工作:
|
| 931 |
不要提到 【逐字稿】 這個詞,直接給出工作內容即可
|
|
|
|
| 932 |
{worksheet_prompt}
|
| 933 |
"""
|
| 934 |
messages = [
|
|
@@ -953,8 +1049,8 @@ def generate_worksheet(password, df_string):
|
|
| 953 |
print("=====worksheet=====")
|
| 954 |
return worksheet, worksheet_prompt
|
| 955 |
|
| 956 |
-
def get_worksheet_prompt():
|
| 957 |
-
|
| 958 |
你是個專業的教師,熟悉布魯姆(Benjamin Bloom, 1964) 的認知理論。布魯姆認為人類的能力,大致可分為三個領域(domains),即認知領域(cognitive domain)、情意領域(affective domain)、技能領域 (psychomotor domain)。
|
| 959 |
|
| 960 |
【認知領域】涉及知能及其運作,著重心智、學習以及問題解決的工作。認知目標從簡單的認識或記憶能力到複雜的評鑑能力。大部分的教育目標都屬於這個領域。認知領域的目標分為六個層次,每個層次代表不同的心智功能。
|
|
@@ -965,41 +1061,135 @@ def get_worksheet_prompt():
|
|
| 965 |
- 🌐 綜合:綜合是指將所學到的片斷概念或知識、原理原則或事實,統整成新的整體。用來表示此種能力的行為動詞有:設計、組織、綜合、創造、歸納、聯合等。例:讀完一篇有關防治汙染的文章後,學生能綜合防治汙染的各種方法。
|
| 966 |
- 🏅 評鑑:評鑑是認知目標中最高層次的能力,指依據某項標準做價值的判斷的能力。用來表示此能力的行為動詞有:評鑑、判斷、評論、比較、批判等。例:學生能評斷辯論中的謬論。
|
| 967 |
|
| 968 |
-
學習單包含以下的內容,將以布魯姆教育目標來建構提問的架構;請用 markdown
|
| 969 |
- 📝 主題:請使用上傳檔案的檔名作為標題
|
| 970 |
- 🔑 重點: 和影片有關重要知識摘要
|
| 971 |
- 💭 概念:概念性問題 - 布魯姆的知識層級;數學知識的建構
|
| 972 |
- 📊 計算:程序性問題 - 布魯姆的理解層級;和影片相同的例題,類似的練習題 → 計算與步驟操作
|
| 973 |
- 🚀 延伸與應用 - 布魯姆的應用、分析、綜合、評鑑層級 → 延伸思考與應用
|
| 974 |
|
| 975 |
-
其中,「重點」的題目請用挖空的填空題;在「計算」的程序性問題請以單選題或填空題的形式來建立,需要 3
|
|
|
|
| 976 |
|
| 977 |
這是範例格式:
|
| 978 |
-
|
| 979 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 980 |
狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
|
| 981 |
|
| 982 |
-
|
| 983 |
-
公里是用來測量長距離的單位,通常用於測量很遠的距離。
|
| 984 |
-
1公里等於___公尺,也稱為千米。
|
| 985 |
-
公里的英文簡寫是 ____。
|
| 986 |
|
| 987 |
-
|
| 988 |
-
請問公里通常用於測量什麼類型的距離?
|
| 989 |
-
如果一圈操場是200公尺,那跑5圈是多少公尺?多少公里?
|
| 990 |
-
為什麼我們需要使用公里這個單位來測量距離?
|
| 991 |
|
| 992 |
-
|
| 993 |
-
一圈操場是200公尺,跑10圈是多少公里?(A) 1公里 (B) 2公里 (C) 3公里 (D) 4公里
|
| 994 |
-
如果你跑了5圈操場,運動手環上會顯示你跑了多少公里?
|
| 995 |
-
6000公尺等於多少公里?
|
| 996 |
|
| 997 |
-
|
| 998 |
-
假設你參加一場慈善路跑活動,全程是5公里。如果你已經跑了3公里,還剩下多少公里?你覺得這樣的活動對你的體能有什麼影響?
|
| 999 |
"""
|
| 1000 |
|
| 1001 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1002 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1003 |
def generate_exam_fine_tune_result(password, worksheet_prompt, df_string_output, exam_result, exam_result_fine_tune_prompt):
|
| 1004 |
verify_password(password)
|
| 1005 |
|
|
@@ -1196,27 +1386,23 @@ def respond_with_jutor_chat(password, user_message, data, chat_history, socratic
|
|
| 1196 |
else:
|
| 1197 |
pass
|
| 1198 |
|
| 1199 |
-
|
| 1200 |
-
|
| 1201 |
-
|
| 1202 |
-
"
|
|
|
|
|
|
|
|
|
|
| 1203 |
"data": {
|
| 1204 |
-
"model": "gpt-4-1106-preview", # 或其他模型
|
| 1205 |
"messages": messages,
|
| 1206 |
-
"max_tokens":
|
|
|
|
|
|
|
|
|
|
| 1207 |
}
|
| 1208 |
}
|
| 1209 |
|
| 1210 |
-
|
| 1211 |
-
response = requests.post(
|
| 1212 |
-
"https://www.junyiacademy.com/api/v2/jutor/chat", # 你的远程API URL
|
| 1213 |
-
headers={
|
| 1214 |
-
"Content-Type": "application/json",
|
| 1215 |
-
# 如果API需要XSRF token或其他认证信息,请在这里添加
|
| 1216 |
-
# 'X-KA-FKey': xsrfToken
|
| 1217 |
-
},
|
| 1218 |
-
json=request_payload # 使用json参数直接发送JSON格式的数据
|
| 1219 |
-
)
|
| 1220 |
|
| 1221 |
if response.status_code == 200:
|
| 1222 |
# 处理响应数据
|
|
@@ -1542,21 +1728,9 @@ with gr.Blocks() as demo:
|
|
| 1542 |
web_link = gr.Textbox(label="Enter Web Page Link", visible=False)
|
| 1543 |
with gr.Tab("學生版"):
|
| 1544 |
with gr.Row():
|
| 1545 |
-
with gr.Column(scale=2):
|
| 1546 |
-
gr.Markdown("## OPEN AI 模式")
|
| 1547 |
-
chatbot = gr.Chatbot()
|
| 1548 |
-
thread_id = gr.Textbox(label="thread_id", visible=False)
|
| 1549 |
-
socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=False)
|
| 1550 |
-
msg = gr.Textbox(label="Message")
|
| 1551 |
-
send_button = gr.Button("Send")
|
| 1552 |
-
gr.Markdown("## GROQ 模式")
|
| 1553 |
-
groq_chatbot = gr.Chatbot(label="groq mode chatbot")
|
| 1554 |
-
groq_msg = gr.Textbox(label="Message")
|
| 1555 |
-
groq_send_button = gr.Button("Send")
|
| 1556 |
-
|
| 1557 |
with gr.Column(scale=3):
|
| 1558 |
-
with gr.Tab("
|
| 1559 |
-
|
| 1560 |
with gr.Tab("重點"):
|
| 1561 |
df_summarise = gr.Textbox(container=True, show_copy_button=True, lines=40)
|
| 1562 |
with gr.Tab("問題"):
|
|
@@ -1566,12 +1740,12 @@ with gr.Blocks() as demo:
|
|
| 1566 |
btn_3 = gr.Button()
|
| 1567 |
gr.Markdown("## 重新生成問題")
|
| 1568 |
btn_create_question = gr.Button("Create Questions")
|
| 1569 |
-
|
| 1570 |
-
mind_map_html = gr.HTML()
|
| 1571 |
-
|
| 1572 |
with gr.Accordion("See Details", open=False):
|
| 1573 |
with gr.Tab("本文"):
|
| 1574 |
df_string_output = gr.Textbox(lines=40, label="Data Text")
|
|
|
|
|
|
|
| 1575 |
with gr.Tab("圖文"):
|
| 1576 |
transcript_html = gr.HTML(label="YouTube Transcript and Video")
|
| 1577 |
with gr.Tab("投影片"):
|
|
@@ -1585,7 +1759,25 @@ with gr.Blocks() as demo:
|
|
| 1585 |
with gr.Tab("markdown"):
|
| 1586 |
gr.Markdown("## 請複製以下 markdown 並貼到你的心智圖工具中,建議使用:https://markmap.js.org/repl")
|
| 1587 |
mind_map = gr.Textbox(container=True, show_copy_button=True, lines=40, elem_id="mind_map_markdown")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1588 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1589 |
|
| 1590 |
with gr.Tab("教師版"):
|
| 1591 |
with gr.Row():
|
|
@@ -1595,7 +1787,11 @@ with gr.Blocks() as demo:
|
|
| 1595 |
# with gr.Tab("認知階層評量題目"):
|
| 1596 |
# cognitive_level_content = gr.Textbox(label="輸入學習目標與內容")
|
| 1597 |
# cognitive_level_content_btn = gr.Button("生成評量題目")
|
| 1598 |
-
with gr.Tab("學習單"):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1599 |
worksheet_content_btn = gr.Button("生成學習單")
|
| 1600 |
with gr.Accordion("prompt", open=False):
|
| 1601 |
worksheet_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
|
@@ -1629,6 +1825,12 @@ with gr.Blocks() as demo:
|
|
| 1629 |
# btn_2.click(respond, inputs=[btn_2, df_string_output, chatbot, socratic_mode_btn], outputs=[msg, chatbot])
|
| 1630 |
# btn_3.click(respond, inputs=[btn_3, df_string_output, chatbot, socratic_mode_btn], outputs=[msg, chatbot])
|
| 1631 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1632 |
# chat_with_youtube_transcript
|
| 1633 |
send_button.click(
|
| 1634 |
chat_with_youtube_transcript,
|
|
@@ -1641,6 +1843,14 @@ with gr.Blocks() as demo:
|
|
| 1641 |
inputs=[password, groq_msg, df_string_output, groq_chatbot, socratic_mode_btn],
|
| 1642 |
outputs=[groq_msg, groq_chatbot]
|
| 1643 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1644 |
# 连接按钮点击事件
|
| 1645 |
btn_1.click(
|
| 1646 |
chat_with_youtube_transcript,
|
|
@@ -1679,9 +1889,10 @@ with gr.Blocks() as demo:
|
|
| 1679 |
transcript_html,
|
| 1680 |
simple_html_content,
|
| 1681 |
slide_image,
|
| 1682 |
-
slide_text
|
| 1683 |
-
|
| 1684 |
-
|
|
|
|
| 1685 |
|
| 1686 |
youtube_link_btn.click(
|
| 1687 |
process_youtube_link,
|
|
@@ -1698,9 +1909,10 @@ with gr.Blocks() as demo:
|
|
| 1698 |
transcript_html,
|
| 1699 |
simple_html_content,
|
| 1700 |
slide_image,
|
| 1701 |
-
slide_text
|
| 1702 |
-
|
| 1703 |
-
|
|
|
|
| 1704 |
|
| 1705 |
# 当输入网页链接时触发
|
| 1706 |
# web_link.change(process_web_link, inputs=web_link, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
|
|
@@ -1708,7 +1920,7 @@ with gr.Blocks() as demo:
|
|
| 1708 |
# 教師版 學習單
|
| 1709 |
worksheet_content_btn.click(
|
| 1710 |
generate_worksheet,
|
| 1711 |
-
inputs=[password, df_string_output],
|
| 1712 |
outputs=[exam_result, worksheet_prompt]
|
| 1713 |
)
|
| 1714 |
exam_result_fine_tune_btn.click(
|
|
|
|
| 65 |
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
| 66 |
GROQ_CLIENT = Groq(api_key=GROQ_API_KEY)
|
| 67 |
|
| 68 |
+
JUTOR_CHAT_KEY = os.getenv("JUTOR_CHAT_KEY")
|
| 69 |
+
|
| 70 |
DRIVE_KEY = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON")
|
| 71 |
GCS_KEY = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON")
|
| 72 |
|
|
|
|
| 477 |
# get_mind_map(video_id, transcript_text, source)
|
| 478 |
# print("===確認其他衍生文件 end ===")
|
| 479 |
|
| 480 |
+
|
| 481 |
# 處理截圖
|
| 482 |
for entry in transcript:
|
| 483 |
if 'img_file_id' not in entry:
|
| 484 |
+
# 檢查 OUTPUT_PATH 是否存在 video_id.mp4
|
| 485 |
+
video_path = f'{OUTPUT_PATH}/{video_id}.mp4'
|
| 486 |
+
if not os.path.exists(video_path):
|
| 487 |
+
# try 5 times 如果都失敗就 raise
|
| 488 |
+
for i in range(5):
|
| 489 |
+
try:
|
| 490 |
+
download_youtube_video(video_id)
|
| 491 |
+
break
|
| 492 |
+
except Exception as e:
|
| 493 |
+
if i == 4:
|
| 494 |
+
raise gr.Error(f"下载视频失败: {str(e)}")
|
| 495 |
+
time.sleep(5)
|
| 496 |
+
# 截图
|
| 497 |
screenshot_path = screenshot_youtube_video(video_id, entry['start'])
|
| 498 |
screenshot_blob_name = f"{video_id}/{video_id}_{entry['start']}.jpg"
|
| 499 |
img_file_id = upload_img_and_get_public_url(gcs_client, bucket_name, screenshot_blob_name, screenshot_path)
|
|
|
|
| 519 |
video_id = extract_youtube_id(link)
|
| 520 |
global VIDEO_ID
|
| 521 |
VIDEO_ID = video_id
|
|
|
|
| 522 |
|
| 523 |
try:
|
| 524 |
# transcript = process_transcript_and_screenshots(video_id)
|
|
|
|
| 575 |
mind_map_json = get_mind_map(video_id, formatted_simple_transcript, source)
|
| 576 |
mind_map = mind_map_json["mind_map"]
|
| 577 |
mind_map_html = get_mind_map_html(mind_map)
|
| 578 |
+
reading_passage_json = get_reading_passage(video_id, formatted_simple_transcript, source)
|
| 579 |
+
reading_passage = reading_passage_json["reading_passage"]
|
| 580 |
|
| 581 |
# 确保返回与 UI 组件预期匹配的输出
|
| 582 |
return video_id, \
|
|
|
|
| 590 |
html_content, \
|
| 591 |
simple_html_content, \
|
| 592 |
first_image, \
|
| 593 |
+
first_text, \
|
| 594 |
+
reading_passage
|
| 595 |
|
| 596 |
def format_transcript_to_html(formatted_transcript):
|
| 597 |
html_content = ""
|
|
|
|
| 647 |
|
| 648 |
|
| 649 |
# ---- LLM Generator ----
|
| 650 |
+
def get_reading_passage(video_id, df_string, source):
|
| 651 |
+
if source == "gcs":
|
| 652 |
+
print("===get_reading_passage on gcs===")
|
| 653 |
+
gcs_client = init_gcs_client(GCS_KEY)
|
| 654 |
+
bucket_name = 'video_ai_assistant'
|
| 655 |
+
file_name = f'{video_id}_reading_passage.json'
|
| 656 |
+
blob_name = f"{video_id}/{file_name}"
|
| 657 |
+
# 检查 reading_passage 是否存在
|
| 658 |
+
is_file_exists = gcs_check_file_exists(gcs_client, bucket_name, blob_name)
|
| 659 |
+
if not is_file_exists:
|
| 660 |
+
reading_passage = generate_reading_passage(df_string)
|
| 661 |
+
reading_passage_json = {"reading_passage": str(reading_passage)}
|
| 662 |
+
reading_passage_text = json.dumps(reading_passage_json, ensure_ascii=False, indent=2)
|
| 663 |
+
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, reading_passage_text)
|
| 664 |
+
print("reading_passage已上传到GCS")
|
| 665 |
+
else:
|
| 666 |
+
# reading_passage已存在,下载内容
|
| 667 |
+
print("reading_passage已存在于GCS中")
|
| 668 |
+
reading_passage_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
|
| 669 |
+
reading_passage_json = json.loads(reading_passage_text)
|
| 670 |
+
|
| 671 |
+
elif source == "drive":
|
| 672 |
+
print("===get_reading_passage on drive===")
|
| 673 |
+
service = init_drive_service()
|
| 674 |
+
parent_folder_id = '1GgI4YVs0KckwStVQkLa1NZ8IpaEMurkL'
|
| 675 |
+
folder_id = create_folder_if_not_exists(service, video_id, parent_folder_id)
|
| 676 |
+
file_name = f'{video_id}_reading_passage.json'
|
| 677 |
+
|
| 678 |
+
# 检查 reading_passage 是否存在
|
| 679 |
+
exists, file_id = check_file_exists(service, folder_id, file_name)
|
| 680 |
+
if not exists:
|
| 681 |
+
reading_passage = generate_reading_passage(df_string)
|
| 682 |
+
reading_passage_json = {"reading_passage": str(reading_passage)}
|
| 683 |
+
reading_passage_text = json.dumps(reading_passage_json, ensure_ascii=False, indent=2)
|
| 684 |
+
upload_content_directly(service, file_name, folder_id, reading_passage_text)
|
| 685 |
+
print("reading_passage已上傳到Google Drive")
|
| 686 |
+
else:
|
| 687 |
+
# reading_passage已存在,下载内容
|
| 688 |
+
print("reading_passage已存在于Google Drive中")
|
| 689 |
+
reading_passage_text = download_file_as_string(service, file_id)
|
| 690 |
+
|
| 691 |
+
return reading_passage_json
|
| 692 |
+
|
| 693 |
+
def generate_reading_passage(df_string):
|
| 694 |
+
# 使用 OpenAI 生成基于上传数据的问题
|
| 695 |
+
sys_content = "你是一個擅長資料分析跟影片教學��老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
|
| 696 |
+
user_content = f"""
|
| 697 |
+
請根據 {df_string}
|
| 698 |
+
文本自行判斷資料的種類
|
| 699 |
+
幫我組合成 Reading Passage
|
| 700 |
+
並潤稿讓文句通順
|
| 701 |
+
請一定要使用 zh-TW
|
| 702 |
+
"""
|
| 703 |
+
messages = [
|
| 704 |
+
{"role": "system", "content": sys_content},
|
| 705 |
+
{"role": "user", "content": user_content}
|
| 706 |
+
]
|
| 707 |
+
|
| 708 |
+
request_payload = {
|
| 709 |
+
"model": "gpt-4-1106-preview",
|
| 710 |
+
"messages": messages,
|
| 711 |
+
"max_tokens": 4000,
|
| 712 |
+
}
|
| 713 |
+
|
| 714 |
+
response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
|
| 715 |
+
reading_passage = response.choices[0].message.content.strip()
|
| 716 |
+
print("=====reading_passage=====")
|
| 717 |
+
print(reading_passage)
|
| 718 |
+
print("=====reading_passage=====")
|
| 719 |
+
|
| 720 |
+
return reading_passage
|
| 721 |
+
|
| 722 |
def get_mind_map(video_id, df_string, source):
|
| 723 |
if source == "gcs":
|
| 724 |
print("===get_mind_map on gcs===")
|
|
|
|
| 1000 |
return q1, q2, q3
|
| 1001 |
|
| 1002 |
# --- 學習單 ---
|
| 1003 |
+
def generate_worksheet(password, df_string, worksheet_topic, worksheet_grade, worksheet_level, worksheet_algorithm):
|
| 1004 |
verify_password(password)
|
| 1005 |
|
| 1006 |
# df_string delete embed_url, screenshot_path
|
|
|
|
| 1010 |
entry.pop('screenshot_path', None)
|
| 1011 |
df_string_text = json.dumps(df_string_json, ensure_ascii=False, indent=2)
|
| 1012 |
|
| 1013 |
+
worksheet_prompt = get_worksheet_prompt(worksheet_algorithm)
|
| 1014 |
|
| 1015 |
# 使用 OpenAI 生成基于上传数据的问题
|
| 1016 |
sys_content = "你是一個擅長資料分析跟影片教學備課的老師,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
|
| 1017 |
user_content = f"""
|
| 1018 |
這是逐字稿:{df_string_text}
|
| 1019 |
---
|
| 1020 |
+
這是學習單模型:{worksheet_algorithm}
|
| 1021 |
+
這是學習單科目:{worksheet_topic}
|
| 1022 |
+
這是學習單年級:{worksheet_grade}
|
| 1023 |
+
這是學習單難度:{worksheet_level}
|
| 1024 |
+
|
| 1025 |
請根據逐字稿進行以下工作:
|
| 1026 |
不要提到 【逐字稿】 這個詞,直接給出工作內容即可
|
| 1027 |
+
如果是中文素材,請嚴格使用 zh-TW
|
| 1028 |
{worksheet_prompt}
|
| 1029 |
"""
|
| 1030 |
messages = [
|
|
|
|
| 1049 |
print("=====worksheet=====")
|
| 1050 |
return worksheet, worksheet_prompt
|
| 1051 |
|
| 1052 |
+
def get_worksheet_prompt(algorithm="Bloom認知階層理論"):
|
| 1053 |
+
bloom_worksheet_prompt = """
|
| 1054 |
你是個專業的教師,熟悉布魯姆(Benjamin Bloom, 1964) 的認知理論。布魯姆認為人類的能力,大致可分為三個領域(domains),即認知領域(cognitive domain)、情意領域(affective domain)、技能領域 (psychomotor domain)。
|
| 1055 |
|
| 1056 |
【認知領域】涉及知能及其運作,著重心智、學習以及問題解決的工作。認知目標從簡單的認識或記憶能力到複雜的評鑑能力。大部分的教育目標都屬於這個領域。認知領域的目標分為六個層次,每個層次代表不同的心智功能。
|
|
|
|
| 1061 |
- 🌐 綜合:綜合是指將所學到的片斷概念或知識、原理原則或事實,統整成新的整體。用來表示此種能力的行為動詞有:設計、組織、綜合、創造、歸納、聯合等。例:讀完一篇有關防治汙染的文章後,學生能綜合防治汙染的各種方法。
|
| 1062 |
- 🏅 評鑑:評鑑是認知目標中最高層次的能力,指依據某項標準做價值的判斷的能力。用來表示此能力的行為動詞有:評鑑、判斷、評論、比較、批判等。例:學生能評斷辯論中的謬論。
|
| 1063 |
|
| 1064 |
+
學習單包含以下的內容,將以布魯姆教育目標來建構提問的架構;請用 markdown 格式來呈現。
|
| 1065 |
- 📝 主題:請使用上傳檔案的檔名作為標題
|
| 1066 |
- 🔑 重點: 和影片有關重要知識摘要
|
| 1067 |
- 💭 概念:概念性問題 - 布魯姆的知識層級;數學知識的建構
|
| 1068 |
- 📊 計算:程序性問題 - 布魯姆的理解層級;和影片相同的例題,類似的練習題 → 計算與步驟操作
|
| 1069 |
- 🚀 延伸與應用 - 布魯姆的應用、分析、綜合、評鑑層級 → 延伸思考與應用
|
| 1070 |
|
| 1071 |
+
其中,「重點」的題目請用挖空的填空題;在「計算」的程序性問題請以單選題或填空題的形式來建立,需要 3 個題目;「延伸與應用���請使用問答題的形式來建立,一題即可。
|
| 1072 |
+
題目和題目之間要換行,並加上 point 符號,像是 "-" 或是 "1." 等等
|
| 1073 |
|
| 1074 |
這是範例格式:
|
| 1075 |
+
🌐 主題:【概念】認識公里
|
| 1076 |
+
|
| 1077 |
+
🎓【對象】
|
| 1078 |
+
科目: 數學
|
| 1079 |
+
年級: 三年級
|
| 1080 |
+
難度: 基礎
|
| 1081 |
+
|
| 1082 |
+
🏞️【情境描述】
|
| 1083 |
狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
|
| 1084 |
|
| 1085 |
+
🔑【影片重點】
|
| 1086 |
+
- 公里是用來測量長距離的單位,通常用於測量很遠的距離。
|
| 1087 |
+
- 1公里等於___公尺,也稱為千米。
|
| 1088 |
+
- 公里的英文簡寫是 ____。
|
| 1089 |
|
| 1090 |
+
🌟【概念】
|
| 1091 |
+
- 請問公里通常用於測量什麼類型的距離?
|
| 1092 |
+
- 如果一圈操場是200公尺,那跑5圈是多少公尺?多少公里?
|
| 1093 |
+
- 為什麼我們需要使用公里這個單位來測量距離?
|
| 1094 |
|
| 1095 |
+
🔢【計算】
|
| 1096 |
+
1. 一圈操場是200公尺,跑10圈是多少公里?(A) 1公里 (B) 2公里 (C) 3公里 (D) 4公里
|
| 1097 |
+
2. 如果你跑了5圈操場,運動手環上會顯示你跑了多少公里?
|
| 1098 |
+
3. 6000公尺等於多少公里?
|
| 1099 |
|
| 1100 |
+
💡【延伸與應用】
|
| 1101 |
+
- 假設你參加一場慈善路跑活動,全程是5公里。如果你已經跑了3公里,還剩下多少公里?你覺得這樣的活動對你的體能有什麼影響?
|
| 1102 |
"""
|
| 1103 |
|
| 1104 |
+
Polya_worksheet_prompt = """
|
| 1105 |
+
你是個專業的教師,熟悉 George Polya(1945) 的數學問題解決策略。
|
| 1106 |
+
Polya提出了一個四步驟的數學問題解決策略,在他影響深遠的經典著作 How to solve it《如何解題》中指出解難過程可分為四個階段:
|
| 1107 |
+
(1) 理解問題 (understanding the problem)
|
| 1108 |
+
(2) 設計解題策略 (devising a plan)
|
| 1109 |
+
(3) 按步解題 (carrying out the plan)
|
| 1110 |
+
(4) 回顧解答 (looking back) (edited)
|
| 1111 |
+
|
| 1112 |
+
請以此設計學習單並依據文本跟難度給予題目
|
| 1113 |
+
請一定要使用 zh-TW
|
| 1114 |
+
|
| 1115 |
+
這是範例格式:
|
| 1116 |
+
🌐 主題:【概念】認識公里
|
| 1117 |
+
|
| 1118 |
+
🎓【對象】
|
| 1119 |
+
科目: 數學
|
| 1120 |
+
年級: 三年級
|
| 1121 |
+
難度: 基礎
|
| 1122 |
+
|
| 1123 |
+
🏞️【情境描述】
|
| 1124 |
+
狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
|
| 1125 |
|
| 1126 |
+
❓【給出問題】
|
| 1127 |
+
- 如果日月潭環潭自行車道共30公里,狐狸貓騎行了13.7公里後休息,剩餘多少公里尚未騎行?
|
| 1128 |
+
|
| 1129 |
+
🤔【理解問題】
|
| 1130 |
+
首先,我們必須完全理解問題的所有細節。在此情境中,我們需要釐清以下幾點:
|
| 1131 |
+
- 日月潭環潭自行車道的總長度為30公里。
|
| 1132 |
+
- 狐狸貓已經騎行了13.7公里。
|
| 1133 |
+
- 我們的目標是計算狐狸貓還剩下多少公里未騎行。
|
| 1134 |
+
|
| 1135 |
+
🧭【設計解題策略】
|
| 1136 |
+
接下來,根據我們對問題的理解來設計解決問題的計劃。在此步驟中,我們決定採用哪種策略來解決問題。對於這個問題,最直接的計劃是使用算術減法:
|
| 1137 |
+
- 總公里數(30公里)減去已騎行的公里數(13.7公里)。
|
| 1138 |
+
|
| 1139 |
+
📝【按步解題】
|
| 1140 |
+
按照設計的計劃執行解題步驟。在這裡我們執行減法運算:
|
| 1141 |
+
30−13.7=16.3
|
| 1142 |
+
這意味著狐狸貓還有16.3公里未騎行。
|
| 1143 |
+
|
| 1144 |
+
✨【回顧解答】
|
| 1145 |
+
最後,解題完成後,我們回顧和反思整個解題過程。在這一步,我們驗證計算的結果是否符合邏輯,是否正確解決了原問題。
|
| 1146 |
+
同時,考慮是否有更有效或簡便的方法來解決類似的問題。在這個例子中,使用直接減法是最簡單直接的方法。
|
| 1147 |
+
但在其他情境下,可能需要考慮使用圖形、表格或代數表達式等其他策略來解決問題。
|
| 1148 |
+
"""
|
| 1149 |
+
|
| 1150 |
+
# CRA教學法
|
| 1151 |
+
cra_worksheet_prompt = """
|
| 1152 |
+
你是個專業的教師,熟悉CRA教學法,CRA教學法是一種教學策略,
|
| 1153 |
+
CRA正是一種用來促進學生學習和記憶數學的三步教學法,它闡明瞭用這種方式進行教學的具體步驟。
|
| 1154 |
+
CRA的三個步驟互相依賴,運用CRA能建立起一種概念結構,從而形成知識的意義關聯。
|
| 1155 |
+
CRA策略的第一個階段, 即實例(C)階段,是一個“做”的階段。在這一階段,教師用加工材料建模,這些材料包括彩色圓片、立方體、十進位積木、六形六色積木,以及分數積木,等等。在使用這些材料時,必須考慮到兒童的視覺、觸覺及動感經驗。
|
| 1156 |
+
階段二,即描述(R)階段,是一個“看”的階段。在這一-階段,具體的模型被改成了圖片展示,教師可用手繪圖片或貼紙來對概念進行解釋。
|
| 1157 |
+
最後一個階段,即抽象(A)階段,是一個抽象的“符號”階段, 在這一階段, 教師使用數字、字母等數學符號(如:2, 6, 3x, +,-等)來進行教學。
|
| 1158 |
+
運用CRA的前提是學生在學習“規則”前必須學會概念。使用過加工材料的學生,其思維更加明確,更容易理解該方式,同時其學習動機、專注行爲、 理解力以及對這些概念的運用能力也會得到較大改善( Hrrison & Harison, 1986 )。
|
| 1159 |
+
CRA策略可以有效地幫助學生理解以下幾個概念:早期數量關係、位值、計算、分數、小數、測量、幾何、貨幣、百分數、數基、應用題、概率以及統計等
|
| 1160 |
+
這是範例格式:
|
| 1161 |
+
🌐 主題:【概念】認識公里
|
| 1162 |
+
|
| 1163 |
+
🎓【對象】
|
| 1164 |
+
科目: 數學
|
| 1165 |
+
年級: 三年級
|
| 1166 |
+
難度: 基礎
|
| 1167 |
+
|
| 1168 |
+
【實例(C)階段】
|
| 1169 |
+
1. 用彩色圓片來解釋什麼是分數?
|
| 1170 |
+
2. 用立方體來解釋什麼是體積?
|
| 1171 |
+
3. 用十進位積木來解釋什麼是小數?
|
| 1172 |
+
|
| 1173 |
+
【描述(R)階段】
|
| 1174 |
+
1. 用手繪圖片來解釋什麼是分數?
|
| 1175 |
+
2. 用貼紙來解釋什麼是體積?
|
| 1176 |
+
3. 用手繪圖片來解釋什麼是小數?
|
| 1177 |
+
|
| 1178 |
+
【抽象(A)階段】
|
| 1179 |
+
1. 用數字來解釋什麼是分數?
|
| 1180 |
+
2. 用字母來解釋什麼是體積?
|
| 1181 |
+
3. 用數字來解釋什麼是小數?
|
| 1182 |
+
|
| 1183 |
+
"""
|
| 1184 |
+
|
| 1185 |
+
case = {
|
| 1186 |
+
"Bloom認知階層理論": bloom_worksheet_prompt,
|
| 1187 |
+
"Polya數學解題法": Polya_worksheet_prompt,
|
| 1188 |
+
"CRA教學法": cra_worksheet_prompt
|
| 1189 |
+
}
|
| 1190 |
+
|
| 1191 |
+
return case[algorithm]
|
| 1192 |
+
|
| 1193 |
def generate_exam_fine_tune_result(password, worksheet_prompt, df_string_output, exam_result, exam_result_fine_tune_prompt):
|
| 1194 |
verify_password(password)
|
| 1195 |
|
|
|
|
| 1386 |
else:
|
| 1387 |
pass
|
| 1388 |
|
| 1389 |
+
messages.append({"role": "user", "content": user_message})
|
| 1390 |
+
api_endpoint = "https://ci-live-feat-video-ai-dot-junyiacademy.appspot.com/api/v2/jutor/hf-chat"
|
| 1391 |
+
headers = {
|
| 1392 |
+
"Content-Type": "application/json",
|
| 1393 |
+
"x-api-key": JUTOR_CHAT_KEY,
|
| 1394 |
+
}
|
| 1395 |
+
data = {
|
| 1396 |
"data": {
|
|
|
|
| 1397 |
"messages": messages,
|
| 1398 |
+
"max_tokens": 512,
|
| 1399 |
+
"temperature": 0.9,
|
| 1400 |
+
"model": "gpt-4-1106-preview",
|
| 1401 |
+
"stream": False,
|
| 1402 |
}
|
| 1403 |
}
|
| 1404 |
|
| 1405 |
+
response = requests.post(api_endpoint, headers=headers, data=json.dumps(data))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1406 |
|
| 1407 |
if response.status_code == 200:
|
| 1408 |
# 处理响应数据
|
|
|
|
| 1728 |
web_link = gr.Textbox(label="Enter Web Page Link", visible=False)
|
| 1729 |
with gr.Tab("學生版"):
|
| 1730 |
with gr.Row():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1731 |
with gr.Column(scale=3):
|
| 1732 |
+
with gr.Tab("文章模式"):
|
| 1733 |
+
reading_passage = gr.Textbox(label="Reading Passage", lines=40)
|
| 1734 |
with gr.Tab("重點"):
|
| 1735 |
df_summarise = gr.Textbox(container=True, show_copy_button=True, lines=40)
|
| 1736 |
with gr.Tab("問題"):
|
|
|
|
| 1740 |
btn_3 = gr.Button()
|
| 1741 |
gr.Markdown("## 重新生成問題")
|
| 1742 |
btn_create_question = gr.Button("Create Questions")
|
| 1743 |
+
|
|
|
|
|
|
|
| 1744 |
with gr.Accordion("See Details", open=False):
|
| 1745 |
with gr.Tab("本文"):
|
| 1746 |
df_string_output = gr.Textbox(lines=40, label="Data Text")
|
| 1747 |
+
with gr.Tab("逐字稿"):
|
| 1748 |
+
simple_html_content = gr.HTML(label="Simple Transcript")
|
| 1749 |
with gr.Tab("圖文"):
|
| 1750 |
transcript_html = gr.HTML(label="YouTube Transcript and Video")
|
| 1751 |
with gr.Tab("投影片"):
|
|
|
|
| 1759 |
with gr.Tab("markdown"):
|
| 1760 |
gr.Markdown("## 請複製以下 markdown 並貼到你的心智圖工具中,建議使用:https://markmap.js.org/repl")
|
| 1761 |
mind_map = gr.Textbox(container=True, show_copy_button=True, lines=40, elem_id="mind_map_markdown")
|
| 1762 |
+
with gr.Tab("心智圖",elem_id="mind_map_tab"):
|
| 1763 |
+
mind_map_html = gr.HTML()
|
| 1764 |
+
with gr.Column(scale=2):
|
| 1765 |
+
gr.Markdown("## OPEN AI 模式")
|
| 1766 |
+
chatbot = gr.Chatbot(avatar_images=["https://assets-global.website-files.com/6405517ab71c76a232456561/6405517ab71c7635804565a3_%E9%A0%AD%E8%B2%BC%20_%E8%80%81%E5%B8%AB%20.png", "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"], label="OPEN AI 模式")
|
| 1767 |
+
thread_id = gr.Textbox(label="thread_id", visible=False)
|
| 1768 |
+
socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True)
|
| 1769 |
+
msg = gr.Textbox(label="Message")
|
| 1770 |
+
send_button = gr.Button("Send")
|
| 1771 |
|
| 1772 |
+
gr.Markdown("## GROQ 模式")
|
| 1773 |
+
groq_chatbot = gr.Chatbot(label="groq mode chatbot")
|
| 1774 |
+
groq_msg = gr.Textbox(label="Message")
|
| 1775 |
+
groq_send_button = gr.Button("Send")
|
| 1776 |
+
|
| 1777 |
+
gr.Markdown("## JUTOR API 模式")
|
| 1778 |
+
jutor_chatbot = gr.Chatbot(label="jutor mode chatbot")
|
| 1779 |
+
jutor_msg = gr.Textbox(label="Message")
|
| 1780 |
+
jutor_send_button = gr.Button("Send")
|
| 1781 |
|
| 1782 |
with gr.Tab("教師版"):
|
| 1783 |
with gr.Row():
|
|
|
|
| 1787 |
# with gr.Tab("認知階層評量題目"):
|
| 1788 |
# cognitive_level_content = gr.Textbox(label="輸入學習目標與內容")
|
| 1789 |
# cognitive_level_content_btn = gr.Button("生成評量題目")
|
| 1790 |
+
with gr.Tab("學習單"):
|
| 1791 |
+
worksheet_topic = gr.Dropdown(label="選擇學習單主題", choices=["數學", "自然", "國文", "英文", "社會"], value="數學")
|
| 1792 |
+
worksheet_grade = gr.Dropdown(label="選擇學習單年級", choices=["一年級", "二年級", "三年級", "四年級", "五年級", "六年級", "七年級", "八年級", "九年級", "十年級", "十一年級", "十二年級"], value="三年級")
|
| 1793 |
+
worksheet_level = gr.Dropdown(label="選擇學習單難度", choices=["基礎", "中級", "進階"], value="基礎")
|
| 1794 |
+
worksheet_algorithm = gr.Dropdown(label="選擇學習單理論", choices=["Bloom認知階層理論", "Polya數學解題法", "CRA教學法"], value="Bloom認知階層理論")
|
| 1795 |
worksheet_content_btn = gr.Button("生成學習單")
|
| 1796 |
with gr.Accordion("prompt", open=False):
|
| 1797 |
worksheet_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
|
|
|
| 1825 |
# btn_2.click(respond, inputs=[btn_2, df_string_output, chatbot, socratic_mode_btn], outputs=[msg, chatbot])
|
| 1826 |
# btn_3.click(respond, inputs=[btn_3, df_string_output, chatbot, socratic_mode_btn], outputs=[msg, chatbot])
|
| 1827 |
|
| 1828 |
+
# chat_with_youtube_transcript
|
| 1829 |
+
# send_button.click(
|
| 1830 |
+
# chat_with_youtube_transcript,
|
| 1831 |
+
# inputs=[password, video_id, thread_id, df_string_output, msg, chatbot, socratic_mode_btn],
|
| 1832 |
+
# outputs=[msg, chatbot, thread_id]
|
| 1833 |
+
# )
|
| 1834 |
# chat_with_youtube_transcript
|
| 1835 |
send_button.click(
|
| 1836 |
chat_with_youtube_transcript,
|
|
|
|
| 1843 |
inputs=[password, groq_msg, df_string_output, groq_chatbot, socratic_mode_btn],
|
| 1844 |
outputs=[groq_msg, groq_chatbot]
|
| 1845 |
)
|
| 1846 |
+
# JUTOR API 模式
|
| 1847 |
+
jutor_send_button.click(
|
| 1848 |
+
respond_with_jutor_chat,
|
| 1849 |
+
inputs=[password, jutor_msg, df_string_output, jutor_chatbot, socratic_mode_btn],
|
| 1850 |
+
outputs=[jutor_msg, jutor_chatbot]
|
| 1851 |
+
)
|
| 1852 |
+
|
| 1853 |
+
|
| 1854 |
# 连接按钮点击事件
|
| 1855 |
btn_1.click(
|
| 1856 |
chat_with_youtube_transcript,
|
|
|
|
| 1889 |
transcript_html,
|
| 1890 |
simple_html_content,
|
| 1891 |
slide_image,
|
| 1892 |
+
slide_text,
|
| 1893 |
+
reading_passage
|
| 1894 |
+
]
|
| 1895 |
+
)
|
| 1896 |
|
| 1897 |
youtube_link_btn.click(
|
| 1898 |
process_youtube_link,
|
|
|
|
| 1909 |
transcript_html,
|
| 1910 |
simple_html_content,
|
| 1911 |
slide_image,
|
| 1912 |
+
slide_text,
|
| 1913 |
+
reading_passage
|
| 1914 |
+
]
|
| 1915 |
+
)
|
| 1916 |
|
| 1917 |
# 当输入网页链接时触发
|
| 1918 |
# web_link.change(process_web_link, inputs=web_link, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
|
|
|
|
| 1920 |
# 教師版 學習單
|
| 1921 |
worksheet_content_btn.click(
|
| 1922 |
generate_worksheet,
|
| 1923 |
+
inputs=[password, df_string_output, worksheet_topic, worksheet_grade, worksheet_level, worksheet_algorithm],
|
| 1924 |
outputs=[exam_result, worksheet_prompt]
|
| 1925 |
)
|
| 1926 |
exam_result_fine_tune_btn.click(
|