Spaces:
Sleeping
Sleeping
update
Browse files- app.py +271 -58
- chatbot.py +1 -1
app.py
CHANGED
@@ -381,7 +381,7 @@ def extract_youtube_id(url):
|
|
381 |
return None
|
382 |
|
383 |
def get_transcript_by_yt_api(video_id):
|
384 |
-
languages = ['zh-TW', 'zh-Hant', 'zh', 'en'] # 優先順序列表
|
385 |
for language in languages:
|
386 |
try:
|
387 |
transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=[language])
|
@@ -582,8 +582,8 @@ def process_youtube_link(password, link):
|
|
582 |
|
583 |
# 基于逐字稿生成其他所需的输出
|
584 |
source = "gcs"
|
585 |
-
|
586 |
-
|
587 |
summary_json = get_video_id_summary(video_id, formatted_simple_transcript, source)
|
588 |
summary_text = summary_json["summary"]
|
589 |
summary = summary_json["summary"]
|
@@ -608,10 +608,7 @@ def process_youtube_link(password, link):
|
|
608 |
|
609 |
# 确保返回与 UI 组件预期匹配的输出
|
610 |
return video_id, \
|
611 |
-
|
612 |
-
questions[0] if len(questions) > 0 else "", \
|
613 |
-
questions[1] if len(questions) > 1 else "", \
|
614 |
-
questions[2] if len(questions) > 2 else "", \
|
615 |
original_transcript, \
|
616 |
summary_text, \
|
617 |
summary, \
|
@@ -888,7 +885,8 @@ def get_video_id_summary(video_id, df_string, source):
|
|
888 |
# 检查 summary_file 是否存在
|
889 |
is_summary_file_exists = GCS_SERVICE.check_file_exists(bucket_name, summary_file_blob_name)
|
890 |
if not is_summary_file_exists:
|
891 |
-
|
|
|
892 |
summary_json = {"summary": str(summary)}
|
893 |
summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
|
894 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, summary_file_blob_name, summary_text)
|
@@ -909,7 +907,8 @@ def get_video_id_summary(video_id, df_string, source):
|
|
909 |
# 检查逐字稿是否存在
|
910 |
exists, file_id = check_file_exists(service, folder_id, file_name)
|
911 |
if not exists:
|
912 |
-
|
|
|
913 |
summary_json = {"summary": str(summary)}
|
914 |
summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
|
915 |
|
@@ -930,12 +929,27 @@ def get_video_id_summary(video_id, df_string, source):
|
|
930 |
|
931 |
return summary_json
|
932 |
|
933 |
-
def generate_summarise(df_string):
|
934 |
# 使用 OpenAI 生成基于上传数据的问题
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
935 |
sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
|
936 |
user_content = f"""
|
937 |
-
|
|
|
|
|
|
|
|
|
|
|
938 |
格式為 Markdown
|
|
|
939 |
整體摘要在一百字以內
|
940 |
重點概念列出 bullet points,至少三個,最多五個
|
941 |
以及可能的結論與結尾延伸小問題提供學生作反思
|
@@ -943,6 +957,7 @@ def generate_summarise(df_string):
|
|
943 |
加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
|
944 |
|
945 |
整體格式為:
|
|
|
946 |
## 📚 整體摘要
|
947 |
- (一個 bullet point....)
|
948 |
|
@@ -955,7 +970,7 @@ def generate_summarise(df_string):
|
|
955 |
- (一個 bullet point....)
|
956 |
|
957 |
## ❓ 延伸小問題
|
958 |
-
- (一個 bullet point
|
959 |
"""
|
960 |
|
961 |
# 🗂️ 1. 內容類型:?
|
@@ -1075,6 +1090,90 @@ def generate_questions(df_string):
|
|
1075 |
|
1076 |
return questions
|
1077 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1078 |
def change_questions(password, df_string):
|
1079 |
verify_password(password)
|
1080 |
|
@@ -1527,6 +1626,16 @@ def update_LLM_content(video_id, new_content, kind):
|
|
1527 |
questions_text = json.dumps(questions_json, ensure_ascii=False, indent=2)
|
1528 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, questions_text)
|
1529 |
updated_content = questions_text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1530 |
|
1531 |
print(f"{kind} 已更新到GCS")
|
1532 |
return gr.update(value=updated_content, interactive=False)
|
@@ -1539,7 +1648,8 @@ def create_LLM_content(video_id, df_string, kind):
|
|
1539 |
content = generate_reading_passage(df_string)
|
1540 |
update_LLM_content(video_id, content, kind)
|
1541 |
elif kind == "summary_markdown":
|
1542 |
-
|
|
|
1543 |
update_LLM_content(video_id, content, kind)
|
1544 |
elif kind == "mind_map":
|
1545 |
content = generate_mind_map(df_string)
|
@@ -1551,17 +1661,26 @@ def create_LLM_content(video_id, df_string, kind):
|
|
1551 |
transcript = df_string
|
1552 |
formatted_simple_transcript = create_formatted_simple_transcript(transcript)
|
1553 |
formatted_transcript = create_formatted_transcript(video_id, transcript)
|
1554 |
-
|
1555 |
-
update_LLM_content(video_id,
|
1556 |
-
content = json.dumps(
|
1557 |
elif kind == "transcript":
|
1558 |
-
|
1559 |
-
update_LLM_content(video_id,
|
1560 |
-
content = json.dumps(
|
1561 |
elif kind == "questions":
|
1562 |
-
|
1563 |
-
update_LLM_content(video_id,
|
1564 |
-
content = json.dumps(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1565 |
|
1566 |
return gr.update(value=content, interactive=False)
|
1567 |
|
@@ -1688,8 +1807,6 @@ def summary_add_markdown_version(video_id):
|
|
1688 |
|
1689 |
return new_summary
|
1690 |
|
1691 |
-
|
1692 |
-
|
1693 |
|
1694 |
# AI 生成教學素材
|
1695 |
def get_meta_data(video_id, source="gcs"):
|
@@ -1838,11 +1955,12 @@ def get_instructions(content_subject, content_grade, key_moments):
|
|
1838 |
subject: {content_subject}
|
1839 |
grade: {content_grade}
|
1840 |
context: {key_moments}
|
1841 |
-
Assistant Role: you are a {content_subject}
|
1842 |
User Role: {content_grade} th-grade student.
|
1843 |
Method: Socratic style, guide thinking, no direct answers. this is very important, please be seriously following.
|
1844 |
Language: Traditional Chinese ZH-TW (it's very important), suitable for {content_grade} th-grade level.
|
1845 |
Response:
|
|
|
1846 |
- Single question, under 100 characters
|
1847 |
- include math symbols (use LaTeX $ to cover before and after, ex: $x^2$)
|
1848 |
- hint with video timestamp which format 【參考:00:00:00】.
|
@@ -1853,12 +1971,32 @@ def get_instructions(content_subject, content_grade, key_moments):
|
|
1853 |
"""
|
1854 |
return instructions
|
1855 |
|
1856 |
-
def chat_with_ai(ai_name, password, video_id, user_data, trascript_state, key_moments, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
|
1857 |
verify_password(password)
|
1858 |
|
1859 |
print("=====user_data=====")
|
1860 |
print(f"user_data: {user_data}")
|
1861 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1862 |
if chat_history is not None and len(chat_history) > 11:
|
1863 |
error_msg = "此次對話超過上限(對話一輪10次)"
|
1864 |
raise gr.Error(error_msg)
|
@@ -1927,7 +2065,7 @@ def chat_with_ai(ai_name, password, video_id, user_data, trascript_state, key_mo
|
|
1927 |
print(f"Error: {e}")
|
1928 |
return "请求失败,请稍后再试!", chat_history
|
1929 |
|
1930 |
-
def chat_with_opan_ai_assistant(password, youtube_id, user_data, thread_id, trascript_state, key_moments, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
|
1931 |
verify_password(password)
|
1932 |
|
1933 |
print("=====user_data=====")
|
@@ -1938,6 +2076,28 @@ def chat_with_opan_ai_assistant(password, youtube_id, user_data, thread_id, tras
|
|
1938 |
error_msg = "你的訊息太長了,請縮短訊息長度至五百字以內"
|
1939 |
raise gr.Error(error_msg)
|
1940 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1941 |
# 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
|
1942 |
if chat_history is not None and len(chat_history) > 10:
|
1943 |
error_msg = "此次對話超過上限(對話一輪10次)"
|
@@ -2000,7 +2160,7 @@ def chat_with_opan_ai_assistant(password, youtube_id, user_data, thread_id, tras
|
|
2000 |
client.beta.threads.messages.create(
|
2001 |
thread_id=thread.id,
|
2002 |
role="user",
|
2003 |
-
content=user_message + "/n 請嚴格遵循instructions,擔任一位蘇格拉底家教,絕對不要重複 user 的問句,請用引導的方式指引方向,請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,請在回答的最後標註【參考:(時):(分):(秒)】,(如果是反問學生,就只問一個問題,請幫助學生更好的理解資料,字數在100
|
2004 |
)
|
2005 |
|
2006 |
# 运行助手,生成响应
|
@@ -2104,13 +2264,13 @@ def poll_run_status(run_id, thread_id, timeout=600, poll_interval=5):
|
|
2104 |
|
2105 |
return run.status
|
2106 |
|
2107 |
-
def
|
2108 |
verify_password(password)
|
2109 |
|
2110 |
print("=====user_data=====")
|
2111 |
print(f"user_data: {user_data}")
|
2112 |
|
2113 |
-
print("===
|
2114 |
print(thread_id)
|
2115 |
|
2116 |
# 先計算 user_message 是否超過 500 個字
|
@@ -2288,7 +2448,7 @@ def init_params(text, request: gr.Request):
|
|
2288 |
password_text, youtube_link, \
|
2289 |
chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor
|
2290 |
|
2291 |
-
def update_state(content_subject, content_grade, trascript, key_moments,
|
2292 |
# inputs=[content_subject, content_grade, df_string_output],
|
2293 |
# outputs=[content_subject_state, content_grade_state, trascript_state]
|
2294 |
content_subject_state = content_subject
|
@@ -2297,13 +2457,22 @@ def update_state(content_subject, content_grade, trascript, key_moments, questio
|
|
2297 |
formatted_simple_transcript = create_formatted_simple_transcript(trascript_json)
|
2298 |
trascript_state = formatted_simple_transcript
|
2299 |
key_moments_state = key_moments
|
2300 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2301 |
ai_chatbot_question_1 = question_1
|
2302 |
ai_chatbot_question_2 = question_2
|
2303 |
ai_chatbot_question_3 = question_3
|
2304 |
|
2305 |
return content_subject_state, content_grade_state, trascript_state, key_moments_state, \
|
2306 |
streaming_chat_thread_id_state, \
|
|
|
2307 |
ai_chatbot_question_1, ai_chatbot_question_2, ai_chatbot_question_3
|
2308 |
|
2309 |
|
@@ -2389,7 +2558,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2389 |
with gr.Tab("AI小精靈"):
|
2390 |
with gr.Accordion("選擇 AI 小精靈", open=True) as chatbot_select_accordion:
|
2391 |
with gr.Row():
|
2392 |
-
with gr.Column(scale=1, variant="panel"):
|
2393 |
chatbot_avatar_url = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
|
2394 |
chatbot_description = """Hi,我是你的AI學伴【飛特精靈】,\n
|
2395 |
我可以陪你一起學習本次的內容,有什麼問題都可以問我喔!\n
|
@@ -2403,7 +2572,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2403 |
gr.Image(value=chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
|
2404 |
chatbot_open_ai_select_btn = gr.Button("👆選擇【飛特精靈】", elem_id="chatbot_btn", visible=True, variant="primary")
|
2405 |
gr.Markdown(value=chatbot_description, visible=True)
|
2406 |
-
with gr.Column(scale=1, variant="panel"):
|
2407 |
streaming_chatbot_avatar_url = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2020/11/1-%E6%98%9F%E7%A9%BA%E9%A0%AD%E8%B2%BC-%E5%A4%AA%E7%A9%BA%E7%8B%90%E7%8B%B8%E8%B2%93-150x150.png"
|
2408 |
streaming_chatbot_description = """Hi,我是【飛特音速】, \n
|
2409 |
說話比較快,但有什麼問題都可以問我喔! \n
|
@@ -2435,7 +2604,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2435 |
bot_avatar = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
|
2436 |
latex_delimiters = [{"left": "$", "right": "$", "display": False}]
|
2437 |
chatbot_greeting = [[
|
2438 |
-
|
2439 |
"""Hi,我是你的AI學伴【飛特精靈】,我可以陪你一起學習本次的內容,有什麼問題都可以問我喔!
|
2440 |
🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!
|
2441 |
🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!
|
@@ -2470,7 +2639,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2470 |
"""
|
2471 |
additional_inputs = [password, video_id, user_data, streaming_chat_thread_id_state, trascript_state, key_moments_state, content_subject_state, content_grade_state]
|
2472 |
streaming_chat = gr.ChatInterface(
|
2473 |
-
fn=
|
2474 |
additional_inputs=additional_inputs,
|
2475 |
submit_btn="送出",
|
2476 |
retry_btn=None,
|
@@ -2482,7 +2651,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2482 |
with gr.Row("其他精靈") as chatbot_jutor:
|
2483 |
with gr.Column():
|
2484 |
ai_chatbot_greeting = [[
|
2485 |
-
|
2486 |
"""Hi,我是飛特精靈的朋友們【梨梨、麥麥、狐狸貓】,也可以陪你一起學習本次的內容,有什麼問題都可以問我喔!
|
2487 |
🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!
|
2488 |
🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!
|
@@ -2491,7 +2660,12 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2491 |
""",
|
2492 |
]]
|
2493 |
ai_chatbot_bot_avatar = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2019/11/%E5%9B%9B%E6%A0%BC%E6%95%85%E4%BA%8B-04.jpg"
|
2494 |
-
ai_name = gr.Dropdown(label="選擇 AI 助理", choices=[
|
|
|
|
|
|
|
|
|
|
|
2495 |
ai_chatbot = gr.Chatbot(avatar_images=[user_avatar, ai_chatbot_bot_avatar], label="ai_chatbot", show_share_button=False, likeable=True, show_label=False, latex_delimiters=latex_delimiters, value=ai_chatbot_greeting)
|
2496 |
ai_chatbot_socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
|
2497 |
with gr.Row():
|
@@ -2526,7 +2700,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2526 |
with gr.Row():
|
2527 |
worksheet_content_type_name = gr.Textbox(value="worksheet", visible=False)
|
2528 |
worksheet_algorithm = gr.Dropdown(label="選擇教學策略或理論", choices=["Bloom認知階層理論", "Polya數學解題法", "CRA教學法"], value="Bloom認知階層理論", visible=False)
|
2529 |
-
worksheet_content_btn = gr.Button("生成學習單 📄", variant="primary")
|
2530 |
with gr.Accordion("微調", open=False):
|
2531 |
worksheet_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2532 |
worksheet_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
@@ -2541,13 +2715,13 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2541 |
worksheet_exam_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
|
2542 |
worksheet_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
2543 |
worksheet_exam_result_word_link = gr.File(label="Download Word")
|
2544 |
-
with gr.Tab("
|
2545 |
with gr.Row():
|
2546 |
with gr.Column(scale=1):
|
2547 |
with gr.Row():
|
2548 |
lesson_plan_content_type_name = gr.Textbox(value="lesson_plan", visible=False)
|
2549 |
lesson_plan_time = gr.Slider(label="選擇課程時間(分鐘)", minimum=10, maximum=120, step=5, value=40)
|
2550 |
-
lesson_plan_btn = gr.Button("
|
2551 |
with gr.Accordion("微調", open=False):
|
2552 |
lesson_plan_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2553 |
lesson_plan_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
@@ -2568,7 +2742,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2568 |
with gr.Row():
|
2569 |
exit_ticket_content_type_name = gr.Textbox(value="exit_ticket", visible=False)
|
2570 |
exit_ticket_time = gr.Slider(label="選擇出場券時間(分鐘)", minimum=5, maximum=10, step=1, value=8)
|
2571 |
-
exit_ticket_btn = gr.Button("生成出場券 🎟️", variant="primary")
|
2572 |
with gr.Accordion("微調", open=False):
|
2573 |
exit_ticket_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2574 |
exit_ticket_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
@@ -2655,9 +2829,19 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2655 |
questions_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2656 |
questions_update_button = gr.Button("儲存", size="sm", variant="primary")
|
2657 |
questions_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2658 |
-
questions_create_button = gr.Button("
|
2659 |
with gr.Row():
|
2660 |
questions_json = gr.Textbox(label="Questions", lines=40, interactive=False, show_copy_button=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2661 |
with gr.Tab("逐字稿"):
|
2662 |
simple_html_content = gr.HTML(label="Simple Transcript")
|
2663 |
with gr.Tab("圖文"):
|
@@ -2687,6 +2871,10 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2687 |
chatbot_select,
|
2688 |
inputs=[chatbot_open_ai_streaming_name],
|
2689 |
outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
|
|
|
|
|
|
|
|
|
2690 |
)
|
2691 |
chatbot_jutor_select_btn.click(
|
2692 |
chatbot_select,
|
@@ -2697,7 +2885,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2697 |
# OPENAI ASSISTANT CHATBOT 模式
|
2698 |
send_button.click(
|
2699 |
chat_with_opan_ai_assistant,
|
2700 |
-
inputs=[password, video_id, user_data, thread_id, trascript_state, key_moments, msg, chatbot, content_subject, content_grade, socratic_mode_btn],
|
2701 |
outputs=[msg, chatbot, thread_id],
|
2702 |
scroll_to_output=True
|
2703 |
)
|
@@ -2707,9 +2895,9 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2707 |
outputs=[msg]
|
2708 |
)
|
2709 |
# OPENAI ASSISTANT CHATBOT 連接按鈕點擊事件
|
2710 |
-
btn_1_chat_with_opan_ai_assistant_input =[password, video_id, user_data, thread_id, trascript_state, key_moments, btn_1, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2711 |
-
btn_2_chat_with_opan_ai_assistant_input =[password, video_id, user_data, thread_id, trascript_state, key_moments, btn_2, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2712 |
-
btn_3_chat_with_opan_ai_assistant_input =[password, video_id, user_data, thread_id, trascript_state, key_moments, btn_3, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2713 |
btn_1.click(
|
2714 |
chat_with_opan_ai_assistant,
|
2715 |
inputs=btn_1_chat_with_opan_ai_assistant_input,
|
@@ -2737,14 +2925,14 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2737 |
# 其他精靈 ai_chatbot 模式
|
2738 |
ai_send_button.click(
|
2739 |
chat_with_ai,
|
2740 |
-
inputs=[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_msg, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn],
|
2741 |
outputs=[ai_msg, ai_chatbot],
|
2742 |
scroll_to_output=True
|
2743 |
)
|
2744 |
# 其他精靈 ai_chatbot 连接按钮点击事件
|
2745 |
-
ai_chatbot_question_1_chat_with_ai_input =[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_chatbot_question_1, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2746 |
-
ai_chatbot_question_2_chat_with_ai_input =[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_chatbot_question_2, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2747 |
-
ai_chatbot_question_3_chat_with_ai_input =[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_chatbot_question_3, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2748 |
ai_chatbot_question_1.click(
|
2749 |
chat_with_ai,
|
2750 |
inputs=ai_chatbot_question_1_chat_with_ai_input,
|
@@ -2771,10 +2959,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2771 |
process_youtube_link_inputs = [password, youtube_link]
|
2772 |
process_youtube_link_outputs = [
|
2773 |
video_id,
|
2774 |
-
|
2775 |
-
btn_1,
|
2776 |
-
btn_2,
|
2777 |
-
btn_3,
|
2778 |
df_string_output,
|
2779 |
summary_text,
|
2780 |
df_summarise,
|
@@ -2796,9 +2981,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2796 |
content_grade,
|
2797 |
df_string_output,
|
2798 |
key_moments,
|
2799 |
-
|
2800 |
-
btn_2,
|
2801 |
-
btn_3
|
2802 |
]
|
2803 |
update_state_outputs = [
|
2804 |
content_subject_state,
|
@@ -2806,6 +2989,9 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2806 |
trascript_state,
|
2807 |
key_moments_state,
|
2808 |
streaming_chat_thread_id_state,
|
|
|
|
|
|
|
2809 |
ai_chatbot_question_1,
|
2810 |
ai_chatbot_question_2,
|
2811 |
ai_chatbot_question_3
|
@@ -2979,6 +3165,33 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2979 |
inputs=[video_id, questions_json, questions_kind],
|
2980 |
outputs=[questions_json]
|
2981 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2982 |
|
2983 |
# 教師版
|
2984 |
worksheet_content_btn.click(
|
|
|
381 |
return None
|
382 |
|
383 |
def get_transcript_by_yt_api(video_id):
|
384 |
+
languages = ['zh-TW', 'zh-Hant', 'zh', 'en-US'] # 優先順序列表
|
385 |
for language in languages:
|
386 |
try:
|
387 |
transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=[language])
|
|
|
582 |
|
583 |
# 基于逐字稿生成其他所需的输出
|
584 |
source = "gcs"
|
585 |
+
questions_answers = get_questions_answers(video_id, formatted_simple_transcript, source)
|
586 |
+
questions_answers_json = json.dumps(questions_answers, ensure_ascii=False, indent=2)
|
587 |
summary_json = get_video_id_summary(video_id, formatted_simple_transcript, source)
|
588 |
summary_text = summary_json["summary"]
|
589 |
summary = summary_json["summary"]
|
|
|
608 |
|
609 |
# 确保返回与 UI 组件预期匹配的输出
|
610 |
return video_id, \
|
611 |
+
questions_answers_json, \
|
|
|
|
|
|
|
612 |
original_transcript, \
|
613 |
summary_text, \
|
614 |
summary, \
|
|
|
885 |
# 检查 summary_file 是否存在
|
886 |
is_summary_file_exists = GCS_SERVICE.check_file_exists(bucket_name, summary_file_blob_name)
|
887 |
if not is_summary_file_exists:
|
888 |
+
meta_data = get_meta_data(video_id)
|
889 |
+
summary = generate_summarise(df_string, meta_data)
|
890 |
summary_json = {"summary": str(summary)}
|
891 |
summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
|
892 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, summary_file_blob_name, summary_text)
|
|
|
907 |
# 检查逐字稿是否存在
|
908 |
exists, file_id = check_file_exists(service, folder_id, file_name)
|
909 |
if not exists:
|
910 |
+
meta_data = get_meta_data(video_id)
|
911 |
+
summary = generate_summarise(df_string, meta_data)
|
912 |
summary_json = {"summary": str(summary)}
|
913 |
summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
|
914 |
|
|
|
929 |
|
930 |
return summary_json
|
931 |
|
932 |
+
def generate_summarise(df_string, metadata=None):
|
933 |
# 使用 OpenAI 生成基于上传数据的问题
|
934 |
+
if metadata:
|
935 |
+
title = metadata.get("title", "")
|
936 |
+
subject = metadata.get("subject", "")
|
937 |
+
grade = metadata.get("grade", "")
|
938 |
+
else:
|
939 |
+
title = ""
|
940 |
+
subject = ""
|
941 |
+
grade = ""
|
942 |
+
|
943 |
sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
|
944 |
user_content = f"""
|
945 |
+
課程名稱:{title}
|
946 |
+
科目:{subject}
|
947 |
+
年級:{grade}
|
948 |
+
|
949 |
+
請根據內文: {df_string}
|
950 |
+
|
951 |
格式為 Markdown
|
952 |
+
如果有課程名稱,請圍繞「課程名稱」為學習重點,進行重點整理,不要整理跟情境故事相關的問題
|
953 |
整體摘要在一百字以內
|
954 |
重點概念列出 bullet points,至少三個,最多五個
|
955 |
以及可能的結論與結尾延伸小問題提供學生作反思
|
|
|
957 |
加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
|
958 |
|
959 |
整體格式為:
|
960 |
+
## 🌟 主題:{{title}} (如果沒有 title 就省略)
|
961 |
## 📚 整體摘要
|
962 |
- (一個 bullet point....)
|
963 |
|
|
|
970 |
- (一個 bullet point....)
|
971 |
|
972 |
## ❓ 延伸小問題
|
973 |
+
- (一個 bullet point....請圍繞「課程名稱」為學習重點,進行重點整理,不要整理跟情境故事相關的問題)
|
974 |
"""
|
975 |
|
976 |
# 🗂️ 1. 內容類型:?
|
|
|
1090 |
|
1091 |
return questions
|
1092 |
|
1093 |
+
def get_questions_answers(video_id, df_string, source="gcs"):
|
1094 |
+
if source == "gcs":
|
1095 |
+
try:
|
1096 |
+
print("===get_questions_answers on gcs===")
|
1097 |
+
gcs_client = GCS_CLIENT
|
1098 |
+
bucket_name = 'video_ai_assistant'
|
1099 |
+
file_name = f'{video_id}_questions_answers.json'
|
1100 |
+
blob_name = f"{video_id}/{file_name}"
|
1101 |
+
# 检查檔案是否存在
|
1102 |
+
is_questions_answers_exists = GCS_SERVICE.check_file_exists(bucket_name, blob_name)
|
1103 |
+
if not is_questions_answers_exists:
|
1104 |
+
questions_answers = generate_questions_answers(df_string)
|
1105 |
+
questions_answers_text = json.dumps(questions_answers, ensure_ascii=False, indent=2)
|
1106 |
+
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, questions_answers_text)
|
1107 |
+
print("questions_answers已上傳到GCS")
|
1108 |
+
else:
|
1109 |
+
# questions_answers已存在,下载内容
|
1110 |
+
print("questions_answers已存在于GCS中")
|
1111 |
+
questions_answers_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
|
1112 |
+
questions_answers = json.loads(questions_answers_text)
|
1113 |
+
except:
|
1114 |
+
questions = get_questions(video_id, df_string, source)
|
1115 |
+
questions_answers = [{"question": q, "answer": ""} for q in questions]
|
1116 |
+
|
1117 |
+
return questions_answers
|
1118 |
+
|
1119 |
+
def generate_questions_answers(df_string):
|
1120 |
+
content_text = str(df_string)
|
1121 |
+
sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,並用既有資料為本質猜測用戶可能會問的問題,使用 zh-TW"
|
1122 |
+
user_content = f"""
|
1123 |
+
請根據 {content_text} 生成三個問題跟答案,主要與學科有關,不要問跟情節故事相關的問題
|
1124 |
+
答案要在最後標示出處【參考:00:01:05】,請根據時間軸 start_time 來標示
|
1125 |
+
請確保問題跟答案都是繁體中文 zh-TW
|
1126 |
+
答案不用是標準答案,而是帶有啟發性的蘇格拉底式問答,讓學生思考本來的問題,以及該去參考的時間點
|
1127 |
+
並用 JSON 格式返回 questions_answers: [{{question: q1的敘述text, answer: q1的答案text}}, ...]
|
1128 |
+
k-v pair 的 key 是 question, value 是 answer
|
1129 |
+
"""
|
1130 |
+
|
1131 |
+
try:
|
1132 |
+
# OPENAI
|
1133 |
+
messages = [
|
1134 |
+
{"role": "system", "content": sys_content},
|
1135 |
+
{"role": "user", "content": user_content}
|
1136 |
+
]
|
1137 |
+
response_format = { "type": "json_object" }
|
1138 |
+
request_payload = {
|
1139 |
+
"model": "gpt-4-turbo",
|
1140 |
+
"messages": messages,
|
1141 |
+
"max_tokens": 4000,
|
1142 |
+
"response_format": response_format
|
1143 |
+
}
|
1144 |
+
|
1145 |
+
response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
|
1146 |
+
questions_answers = json.loads(response.choices[0].message.content)["questions_answers"]
|
1147 |
+
except:
|
1148 |
+
# REDROCK_CLIENT
|
1149 |
+
messages = [
|
1150 |
+
{"role": "user", "content": user_content}
|
1151 |
+
]
|
1152 |
+
model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
|
1153 |
+
# model_id = "anthropic.claude-3-haiku-20240307-v1:0"
|
1154 |
+
kwargs = {
|
1155 |
+
"modelId": model_id,
|
1156 |
+
"contentType": "application/json",
|
1157 |
+
"accept": "application/json",
|
1158 |
+
"body": json.dumps({
|
1159 |
+
"anthropic_version": "bedrock-2023-05-31",
|
1160 |
+
"max_tokens": 4000,
|
1161 |
+
"system": sys_content,
|
1162 |
+
"messages": messages
|
1163 |
+
})
|
1164 |
+
}
|
1165 |
+
response = BEDROCK_CLIENT.invoke_model(**kwargs)
|
1166 |
+
response_body = json.loads(response.get('body').read())
|
1167 |
+
response_completion = response_body.get('content')[0].get('text')
|
1168 |
+
questions_answers = json.loads(response_completion)["questions_answers"]
|
1169 |
+
|
1170 |
+
print("=====json_response=====")
|
1171 |
+
print(questions_answers)
|
1172 |
+
print("=====json_response=====")
|
1173 |
+
|
1174 |
+
return questions_answers
|
1175 |
+
|
1176 |
+
|
1177 |
def change_questions(password, df_string):
|
1178 |
verify_password(password)
|
1179 |
|
|
|
1626 |
questions_text = json.dumps(questions_json, ensure_ascii=False, indent=2)
|
1627 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, questions_text)
|
1628 |
updated_content = questions_text
|
1629 |
+
elif kind == "questions_answers":
|
1630 |
+
# from update_LLM_btn -> new_content is a string
|
1631 |
+
# create_LLM_content -> new_content is a list
|
1632 |
+
if isinstance(new_content, str):
|
1633 |
+
questions_answers_json = json.loads(new_content)
|
1634 |
+
else:
|
1635 |
+
questions_answers_json = new_content
|
1636 |
+
questions_answers_text = json.dumps(questions_answers_json, ensure_ascii=False, indent=2)
|
1637 |
+
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, questions_answers_text)
|
1638 |
+
updated_content = questions_answers_text
|
1639 |
|
1640 |
print(f"{kind} 已更新到GCS")
|
1641 |
return gr.update(value=updated_content, interactive=False)
|
|
|
1648 |
content = generate_reading_passage(df_string)
|
1649 |
update_LLM_content(video_id, content, kind)
|
1650 |
elif kind == "summary_markdown":
|
1651 |
+
meta_data = get_meta_data(video_id)
|
1652 |
+
content = generate_summarise(df_string, meta_data)
|
1653 |
update_LLM_content(video_id, content, kind)
|
1654 |
elif kind == "mind_map":
|
1655 |
content = generate_mind_map(df_string)
|
|
|
1661 |
transcript = df_string
|
1662 |
formatted_simple_transcript = create_formatted_simple_transcript(transcript)
|
1663 |
formatted_transcript = create_formatted_transcript(video_id, transcript)
|
1664 |
+
gen_content = generate_key_moments(formatted_simple_transcript, formatted_transcript)
|
1665 |
+
update_LLM_content(video_id, gen_content, kind)
|
1666 |
+
content = json.dumps(gen_content, ensure_ascii=False, indent=2)
|
1667 |
elif kind == "transcript":
|
1668 |
+
gen_content = process_transcript_and_screenshots_on_gcs(video_id)
|
1669 |
+
update_LLM_content(video_id, gen_content, kind)
|
1670 |
+
content = json.dumps(gen_content, ensure_ascii=False, indent=2)
|
1671 |
elif kind == "questions":
|
1672 |
+
gen_content = generate_questions(df_string)
|
1673 |
+
update_LLM_content(video_id, gen_content, kind)
|
1674 |
+
content = json.dumps(gen_content, ensure_ascii=False, indent=2)
|
1675 |
+
elif kind == "questions_answers":
|
1676 |
+
if isinstance(df_string, str):
|
1677 |
+
transcript = json.loads(df_string)
|
1678 |
+
else:
|
1679 |
+
transcript = df_string
|
1680 |
+
formatted_simple_transcript = create_formatted_simple_transcript(transcript)
|
1681 |
+
gen_content = generate_questions_answers(formatted_simple_transcript)
|
1682 |
+
update_LLM_content(video_id, gen_content, kind)
|
1683 |
+
content = json.dumps(gen_content, ensure_ascii=False, indent=2)
|
1684 |
|
1685 |
return gr.update(value=content, interactive=False)
|
1686 |
|
|
|
1807 |
|
1808 |
return new_summary
|
1809 |
|
|
|
|
|
1810 |
|
1811 |
# AI 生成教學素材
|
1812 |
def get_meta_data(video_id, source="gcs"):
|
|
|
1955 |
subject: {content_subject}
|
1956 |
grade: {content_grade}
|
1957 |
context: {key_moments}
|
1958 |
+
Assistant Role: you are a {content_subject} assistant. you can call yourself as {content_subject} 學伴
|
1959 |
User Role: {content_grade} th-grade student.
|
1960 |
Method: Socratic style, guide thinking, no direct answers. this is very important, please be seriously following.
|
1961 |
Language: Traditional Chinese ZH-TW (it's very important), suitable for {content_grade} th-grade level.
|
1962 |
Response:
|
1963 |
+
- if user say hi or hello or any greeting, just say hi back and introduce yourself. Then ask user to ask question in context.
|
1964 |
- Single question, under 100 characters
|
1965 |
- include math symbols (use LaTeX $ to cover before and after, ex: $x^2$)
|
1966 |
- hint with video timestamp which format 【參考:00:00:00】.
|
|
|
1971 |
"""
|
1972 |
return instructions
|
1973 |
|
1974 |
+
def chat_with_ai(ai_name, password, video_id, user_data, trascript_state, key_moments, user_message, chat_history, content_subject, content_grade, questions_answers_json, socratic_mode=False):
|
1975 |
verify_password(password)
|
1976 |
|
1977 |
print("=====user_data=====")
|
1978 |
print(f"user_data: {user_data}")
|
1979 |
|
1980 |
+
questions_answers_json = json.loads(questions_answers_json)
|
1981 |
+
for qa in questions_answers_json:
|
1982 |
+
question = qa["question"]
|
1983 |
+
answer = qa["answer"]
|
1984 |
+
if user_message == question and answer != "":
|
1985 |
+
print("=== in questions_answers_json==")
|
1986 |
+
print(f"question: {question}")
|
1987 |
+
print(f"answer: {answer}")
|
1988 |
+
# 更新聊天历史
|
1989 |
+
new_chat_history = (user_message, answer)
|
1990 |
+
if chat_history is None:
|
1991 |
+
chat_history = [new_chat_history]
|
1992 |
+
else:
|
1993 |
+
chat_history.append(new_chat_history)
|
1994 |
+
|
1995 |
+
# 等待 3 秒
|
1996 |
+
time.sleep(3)
|
1997 |
+
|
1998 |
+
return "", chat_history
|
1999 |
+
|
2000 |
if chat_history is not None and len(chat_history) > 11:
|
2001 |
error_msg = "此次對話超過上限(對話一輪10次)"
|
2002 |
raise gr.Error(error_msg)
|
|
|
2065 |
print(f"Error: {e}")
|
2066 |
return "请求失败,请稍后再试!", chat_history
|
2067 |
|
2068 |
+
def chat_with_opan_ai_assistant(password, youtube_id, user_data, thread_id, trascript_state, key_moments, user_message, chat_history, content_subject, content_grade, questions_answers_json, socratic_mode=False):
|
2069 |
verify_password(password)
|
2070 |
|
2071 |
print("=====user_data=====")
|
|
|
2076 |
error_msg = "你的訊息太長了,請縮短訊息長度至五百字以內"
|
2077 |
raise gr.Error(error_msg)
|
2078 |
|
2079 |
+
questions_answers_json = json.loads(questions_answers_json)
|
2080 |
+
for qa in questions_answers_json:
|
2081 |
+
question = qa["question"]
|
2082 |
+
answer = qa["answer"]
|
2083 |
+
if user_message == question and answer != "":
|
2084 |
+
print("=== in questions_answers_json==")
|
2085 |
+
print(f"question: {question}")
|
2086 |
+
print(f"answer: {answer}")
|
2087 |
+
print(f"thread_id: {thread_id}")
|
2088 |
+
# 更新聊天历史
|
2089 |
+
new_chat_history = (user_message, answer)
|
2090 |
+
if chat_history is None:
|
2091 |
+
chat_history = [new_chat_history]
|
2092 |
+
else:
|
2093 |
+
chat_history.append(new_chat_history)
|
2094 |
+
|
2095 |
+
# 等待 3 秒
|
2096 |
+
time.sleep(3)
|
2097 |
+
|
2098 |
+
return "", chat_history, thread_id
|
2099 |
+
|
2100 |
+
|
2101 |
# 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
|
2102 |
if chat_history is not None and len(chat_history) > 10:
|
2103 |
error_msg = "此次對話超過上限(對話一輪10次)"
|
|
|
2160 |
client.beta.threads.messages.create(
|
2161 |
thread_id=thread.id,
|
2162 |
role="user",
|
2163 |
+
content=user_message + "/n 請嚴格遵循instructions,擔任一位蘇格拉底家教,絕對不要重複 user 的問句,請用引導的方式指引方向,請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,請在回答的最後標註【參考:(時):(分):(秒)】,(如果是反問學生,就只問一個問題,請幫助學生更好的理解資料,字數在100字以內,回答時如果講到數學專有名詞,請用數學符號代替文字(Latex 用 $ 字號 render, ex: $x^2$)"
|
2164 |
)
|
2165 |
|
2166 |
# 运行助手,生成响应
|
|
|
2264 |
|
2265 |
return run.status
|
2266 |
|
2267 |
+
def chat_with_opan_ai_assistant_streaming(user_message, chat_history, password, video_id, user_data, thread_id, trascript, key_moments, content_subject, content_grade):
|
2268 |
verify_password(password)
|
2269 |
|
2270 |
print("=====user_data=====")
|
2271 |
print(f"user_data: {user_data}")
|
2272 |
|
2273 |
+
print("===chat_with_opan_ai_assistant_streaming===")
|
2274 |
print(thread_id)
|
2275 |
|
2276 |
# 先計算 user_message 是否超過 500 個字
|
|
|
2448 |
password_text, youtube_link, \
|
2449 |
chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor
|
2450 |
|
2451 |
+
def update_state(content_subject, content_grade, trascript, key_moments, questions_answers):
|
2452 |
# inputs=[content_subject, content_grade, df_string_output],
|
2453 |
# outputs=[content_subject_state, content_grade_state, trascript_state]
|
2454 |
content_subject_state = content_subject
|
|
|
2457 |
formatted_simple_transcript = create_formatted_simple_transcript(trascript_json)
|
2458 |
trascript_state = formatted_simple_transcript
|
2459 |
key_moments_state = key_moments
|
2460 |
+
|
2461 |
+
streaming_chat_thread_id_state = ""
|
2462 |
+
questions_answers_json = json.loads(questions_answers)
|
2463 |
+
question_1 = questions_answers_json[0]["question"]
|
2464 |
+
question_2 = questions_answers_json[1]["question"]
|
2465 |
+
question_3 = questions_answers_json[2]["question"]
|
2466 |
+
btn_1 = question_1
|
2467 |
+
btn_2 = question_2
|
2468 |
+
btn_3 = question_3
|
2469 |
ai_chatbot_question_1 = question_1
|
2470 |
ai_chatbot_question_2 = question_2
|
2471 |
ai_chatbot_question_3 = question_3
|
2472 |
|
2473 |
return content_subject_state, content_grade_state, trascript_state, key_moments_state, \
|
2474 |
streaming_chat_thread_id_state, \
|
2475 |
+
btn_1, btn_2, btn_3, \
|
2476 |
ai_chatbot_question_1, ai_chatbot_question_2, ai_chatbot_question_3
|
2477 |
|
2478 |
|
|
|
2558 |
with gr.Tab("AI小精靈"):
|
2559 |
with gr.Accordion("選擇 AI 小精靈", open=True) as chatbot_select_accordion:
|
2560 |
with gr.Row():
|
2561 |
+
with gr.Column(scale=1, variant="panel", visible=False):
|
2562 |
chatbot_avatar_url = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
|
2563 |
chatbot_description = """Hi,我是你的AI學伴【飛特精靈】,\n
|
2564 |
我可以陪你一起學習本次的內容,有什麼問題都可以問我喔!\n
|
|
|
2572 |
gr.Image(value=chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
|
2573 |
chatbot_open_ai_select_btn = gr.Button("👆選擇【飛特精靈】", elem_id="chatbot_btn", visible=True, variant="primary")
|
2574 |
gr.Markdown(value=chatbot_description, visible=True)
|
2575 |
+
with gr.Column(scale=1, variant="panel", visible=False):
|
2576 |
streaming_chatbot_avatar_url = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2020/11/1-%E6%98%9F%E7%A9%BA%E9%A0%AD%E8%B2%BC-%E5%A4%AA%E7%A9%BA%E7%8B%90%E7%8B%B8%E8%B2%93-150x150.png"
|
2577 |
streaming_chatbot_description = """Hi,我是【飛特音速】, \n
|
2578 |
說話比較快,但有什麼問題都可以問我喔! \n
|
|
|
2604 |
bot_avatar = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
|
2605 |
latex_delimiters = [{"left": "$", "right": "$", "display": False}]
|
2606 |
chatbot_greeting = [[
|
2607 |
+
"請問你是誰?",
|
2608 |
"""Hi,我是你的AI學伴【飛特精靈】,我可以陪你一起學習本次的內容,有什麼問題都可以問我喔!
|
2609 |
🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!
|
2610 |
🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!
|
|
|
2639 |
"""
|
2640 |
additional_inputs = [password, video_id, user_data, streaming_chat_thread_id_state, trascript_state, key_moments_state, content_subject_state, content_grade_state]
|
2641 |
streaming_chat = gr.ChatInterface(
|
2642 |
+
fn=chat_with_opan_ai_assistant_streaming,
|
2643 |
additional_inputs=additional_inputs,
|
2644 |
submit_btn="送出",
|
2645 |
retry_btn=None,
|
|
|
2651 |
with gr.Row("其他精靈") as chatbot_jutor:
|
2652 |
with gr.Column():
|
2653 |
ai_chatbot_greeting = [[
|
2654 |
+
"請問你是誰?",
|
2655 |
"""Hi,我是飛特精靈的朋友們【梨梨、麥麥、狐狸貓】,也可以陪你一起學習本次的內容,有什麼問題都可以問我喔!
|
2656 |
🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!
|
2657 |
🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!
|
|
|
2660 |
""",
|
2661 |
]]
|
2662 |
ai_chatbot_bot_avatar = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2019/11/%E5%9B%9B%E6%A0%BC%E6%95%85%E4%BA%8B-04.jpg"
|
2663 |
+
ai_name = gr.Dropdown(label="選擇 AI 助理", choices=[
|
2664 |
+
# ("梨梨","jutor"),
|
2665 |
+
("麥麥","claude3"),
|
2666 |
+
("狐狸貓","groq")],
|
2667 |
+
value="claude3"
|
2668 |
+
)
|
2669 |
ai_chatbot = gr.Chatbot(avatar_images=[user_avatar, ai_chatbot_bot_avatar], label="ai_chatbot", show_share_button=False, likeable=True, show_label=False, latex_delimiters=latex_delimiters, value=ai_chatbot_greeting)
|
2670 |
ai_chatbot_socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
|
2671 |
with gr.Row():
|
|
|
2700 |
with gr.Row():
|
2701 |
worksheet_content_type_name = gr.Textbox(value="worksheet", visible=False)
|
2702 |
worksheet_algorithm = gr.Dropdown(label="選擇教學策略或理論", choices=["Bloom認知階層理論", "Polya數學解題法", "CRA教學法"], value="Bloom認知階層理論", visible=False)
|
2703 |
+
worksheet_content_btn = gr.Button("生成學習單 📄", variant="primary", visible=False)
|
2704 |
with gr.Accordion("微調", open=False):
|
2705 |
worksheet_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2706 |
worksheet_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
|
|
2715 |
worksheet_exam_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
|
2716 |
worksheet_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
2717 |
worksheet_exam_result_word_link = gr.File(label="Download Word")
|
2718 |
+
with gr.Tab("教案"):
|
2719 |
with gr.Row():
|
2720 |
with gr.Column(scale=1):
|
2721 |
with gr.Row():
|
2722 |
lesson_plan_content_type_name = gr.Textbox(value="lesson_plan", visible=False)
|
2723 |
lesson_plan_time = gr.Slider(label="選擇課程時間(分鐘)", minimum=10, maximum=120, step=5, value=40)
|
2724 |
+
lesson_plan_btn = gr.Button("生成教案 📕", variant="primary", visible=False)
|
2725 |
with gr.Accordion("微調", open=False):
|
2726 |
lesson_plan_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2727 |
lesson_plan_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
|
|
2742 |
with gr.Row():
|
2743 |
exit_ticket_content_type_name = gr.Textbox(value="exit_ticket", visible=False)
|
2744 |
exit_ticket_time = gr.Slider(label="選擇出場券時間(分鐘)", minimum=5, maximum=10, step=1, value=8)
|
2745 |
+
exit_ticket_btn = gr.Button("生成出場券 🎟️", variant="primary", visible=False)
|
2746 |
with gr.Accordion("微調", open=False):
|
2747 |
exit_ticket_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2748 |
exit_ticket_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
|
|
2829 |
questions_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2830 |
questions_update_button = gr.Button("儲存", size="sm", variant="primary")
|
2831 |
questions_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2832 |
+
questions_create_button = gr.Button("重建", size="sm", variant="primary")
|
2833 |
with gr.Row():
|
2834 |
questions_json = gr.Textbox(label="Questions", lines=40, interactive=False, show_copy_button=True)
|
2835 |
+
with gr.Tab("問題答案本文"):
|
2836 |
+
with gr.Row() as questions_answers_admin:
|
2837 |
+
questions_answers_kind = gr.Textbox(value="questions_answers", show_label=False)
|
2838 |
+
questions_answers_get_button = gr.Button("取得", size="sm", variant="primary")
|
2839 |
+
questions_answers_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2840 |
+
questions_answers_update_button = gr.Button("儲存", size="sm", variant="primary")
|
2841 |
+
questions_answers_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2842 |
+
questions_answers_create_button = gr.Button("重建", size="sm", variant="primary")
|
2843 |
+
with gr.Row():
|
2844 |
+
questions_answers_json = gr.Textbox(label="Questions Answers", lines=40, interactive=False, show_copy_button=True)
|
2845 |
with gr.Tab("逐字稿"):
|
2846 |
simple_html_content = gr.HTML(label="Simple Transcript")
|
2847 |
with gr.Tab("圖文"):
|
|
|
2871 |
chatbot_select,
|
2872 |
inputs=[chatbot_open_ai_streaming_name],
|
2873 |
outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
|
2874 |
+
).then(
|
2875 |
+
create_thread_id,
|
2876 |
+
inputs=[],
|
2877 |
+
outputs=[streaming_chat_thread_id_state]
|
2878 |
)
|
2879 |
chatbot_jutor_select_btn.click(
|
2880 |
chatbot_select,
|
|
|
2885 |
# OPENAI ASSISTANT CHATBOT 模式
|
2886 |
send_button.click(
|
2887 |
chat_with_opan_ai_assistant,
|
2888 |
+
inputs=[password, video_id, user_data, thread_id, trascript_state, key_moments, msg, chatbot, content_subject, content_grade, questions_answers_json, socratic_mode_btn],
|
2889 |
outputs=[msg, chatbot, thread_id],
|
2890 |
scroll_to_output=True
|
2891 |
)
|
|
|
2895 |
outputs=[msg]
|
2896 |
)
|
2897 |
# OPENAI ASSISTANT CHATBOT 連接按鈕點擊事件
|
2898 |
+
btn_1_chat_with_opan_ai_assistant_input =[password, video_id, user_data, thread_id, trascript_state, key_moments, btn_1, chatbot, content_subject, content_grade, questions_answers_json, ai_chatbot_socratic_mode_btn]
|
2899 |
+
btn_2_chat_with_opan_ai_assistant_input =[password, video_id, user_data, thread_id, trascript_state, key_moments, btn_2, chatbot, content_subject, content_grade, questions_answers_json, ai_chatbot_socratic_mode_btn]
|
2900 |
+
btn_3_chat_with_opan_ai_assistant_input =[password, video_id, user_data, thread_id, trascript_state, key_moments, btn_3, chatbot, content_subject, content_grade, questions_answers_json, ai_chatbot_socratic_mode_btn]
|
2901 |
btn_1.click(
|
2902 |
chat_with_opan_ai_assistant,
|
2903 |
inputs=btn_1_chat_with_opan_ai_assistant_input,
|
|
|
2925 |
# 其他精靈 ai_chatbot 模式
|
2926 |
ai_send_button.click(
|
2927 |
chat_with_ai,
|
2928 |
+
inputs=[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_msg, ai_chatbot, content_subject, content_grade, questions_answers_json, ai_chatbot_socratic_mode_btn],
|
2929 |
outputs=[ai_msg, ai_chatbot],
|
2930 |
scroll_to_output=True
|
2931 |
)
|
2932 |
# 其他精靈 ai_chatbot 连接按钮点击事件
|
2933 |
+
ai_chatbot_question_1_chat_with_ai_input =[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_chatbot_question_1, ai_chatbot, content_subject, content_grade, questions_answers_json, ai_chatbot_socratic_mode_btn]
|
2934 |
+
ai_chatbot_question_2_chat_with_ai_input =[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_chatbot_question_2, ai_chatbot, content_subject, content_grade, questions_answers_json, ai_chatbot_socratic_mode_btn]
|
2935 |
+
ai_chatbot_question_3_chat_with_ai_input =[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_chatbot_question_3, ai_chatbot, content_subject, content_grade, questions_answers_json, ai_chatbot_socratic_mode_btn]
|
2936 |
ai_chatbot_question_1.click(
|
2937 |
chat_with_ai,
|
2938 |
inputs=ai_chatbot_question_1_chat_with_ai_input,
|
|
|
2959 |
process_youtube_link_inputs = [password, youtube_link]
|
2960 |
process_youtube_link_outputs = [
|
2961 |
video_id,
|
2962 |
+
questions_answers_json,
|
|
|
|
|
|
|
2963 |
df_string_output,
|
2964 |
summary_text,
|
2965 |
df_summarise,
|
|
|
2981 |
content_grade,
|
2982 |
df_string_output,
|
2983 |
key_moments,
|
2984 |
+
questions_answers_json,
|
|
|
|
|
2985 |
]
|
2986 |
update_state_outputs = [
|
2987 |
content_subject_state,
|
|
|
2989 |
trascript_state,
|
2990 |
key_moments_state,
|
2991 |
streaming_chat_thread_id_state,
|
2992 |
+
btn_1,
|
2993 |
+
btn_2,
|
2994 |
+
btn_3,
|
2995 |
ai_chatbot_question_1,
|
2996 |
ai_chatbot_question_2,
|
2997 |
ai_chatbot_question_3
|
|
|
3165 |
inputs=[video_id, questions_json, questions_kind],
|
3166 |
outputs=[questions_json]
|
3167 |
)
|
3168 |
+
# questions_answers event
|
3169 |
+
questions_answers_get_button.click(
|
3170 |
+
get_LLM_content,
|
3171 |
+
inputs=[video_id, questions_answers_kind],
|
3172 |
+
outputs=[questions_answers_json]
|
3173 |
+
)
|
3174 |
+
questions_answers_create_button.click(
|
3175 |
+
create_LLM_content,
|
3176 |
+
inputs=[video_id, df_string_output, questions_answers_kind],
|
3177 |
+
outputs=[questions_answers_json]
|
3178 |
+
)
|
3179 |
+
questions_answers_delete_button.click(
|
3180 |
+
delete_LLM_content,
|
3181 |
+
inputs=[video_id, questions_answers_kind],
|
3182 |
+
outputs=[questions_answers_json]
|
3183 |
+
)
|
3184 |
+
questions_answers_edit_button.click(
|
3185 |
+
enable_edit_mode,
|
3186 |
+
inputs=[],
|
3187 |
+
outputs=[questions_answers_json]
|
3188 |
+
)
|
3189 |
+
questions_answers_update_button.click(
|
3190 |
+
update_LLM_content,
|
3191 |
+
inputs=[video_id, questions_answers_json, questions_answers_kind],
|
3192 |
+
outputs=[questions_answers_json]
|
3193 |
+
)
|
3194 |
+
|
3195 |
|
3196 |
# 教師版
|
3197 |
worksheet_content_btn.click(
|
chatbot.py
CHANGED
@@ -61,7 +61,7 @@ class Chatbot:
|
|
61 |
messages.append({"role": "assistant", "content": assistant_msg})
|
62 |
|
63 |
if user_message:
|
64 |
-
user_message += "/n (請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,不用提到「逐字稿」這個詞,用「內容」代替)
|
65 |
messages.append({"role": "user", "content": user_message})
|
66 |
return messages
|
67 |
|
|
|
61 |
messages.append({"role": "assistant", "content": assistant_msg})
|
62 |
|
63 |
if user_message:
|
64 |
+
user_message += "/n (請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,不用提到「逐字稿」這個詞,用「內容」代替),回答時如果有用到數學式,請用數學符號代替純文字(Latex 用 $ 字號 render)"
|
65 |
messages.append({"role": "user", "content": user_message})
|
66 |
return messages
|
67 |
|