Spaces:
Sleeping
Sleeping
update
Browse files- app.py +236 -75
- chatbot.py +2 -2
- requirements.txt +2 -2
app.py
CHANGED
@@ -489,6 +489,7 @@ def process_transcript_and_screenshots_on_gcs(video_id):
|
|
489 |
transcript_file_name = f'{video_id}_transcript.json'
|
490 |
transcript_blob_name = f"{video_id}/{transcript_file_name}"
|
491 |
# 检查逐字稿是否存在
|
|
|
492 |
is_transcript_exists = GCS_SERVICE.check_file_exists(bucket_name, transcript_blob_name)
|
493 |
if not is_transcript_exists:
|
494 |
# 从YouTube获取逐字稿并上传
|
@@ -507,6 +508,7 @@ def process_transcript_and_screenshots_on_gcs(video_id):
|
|
507 |
|
508 |
transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
|
509 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, transcript_blob_name, transcript_text)
|
|
|
510 |
else:
|
511 |
# 逐字稿已存在,下载逐字稿内容
|
512 |
print("逐字稿已存在于GCS中")
|
@@ -542,15 +544,20 @@ def process_transcript_and_screenshots_on_gcs(video_id):
|
|
542 |
img_file_id = upload_img_and_get_public_url(gcs_client, bucket_name, screenshot_blob_name, screenshot_path)
|
543 |
entry['img_file_id'] = img_file_id
|
544 |
print(f"截图已上传到GCS: {img_file_id}")
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
|
|
|
|
|
|
|
|
|
|
554 |
|
555 |
return updated_transcript_json
|
556 |
|
@@ -691,11 +698,11 @@ def screenshot_youtube_video(youtube_id, snapshot_sec):
|
|
691 |
|
692 |
|
693 |
# ---- Web ----
|
694 |
-
def process_web_link(link):
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
|
700 |
|
701 |
# ---- LLM Generator ----
|
@@ -1766,6 +1773,104 @@ def poll_run_status(run_id, thread_id, timeout=600, poll_interval=5):
|
|
1766 |
|
1767 |
return run.status
|
1768 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1769 |
# --- Slide mode ---
|
1770 |
def update_slide(direction):
|
1771 |
global TRANSCRIPTS
|
@@ -1794,6 +1899,8 @@ def prev_slide():
|
|
1794 |
def next_slide():
|
1795 |
return update_slide(1)
|
1796 |
|
|
|
|
|
1797 |
def init_params(text, request: gr.Request):
|
1798 |
if request:
|
1799 |
print("Request headers dictionary:", request.headers)
|
@@ -1826,6 +1933,21 @@ def init_params(text, request: gr.Request):
|
|
1826 |
|
1827 |
return admin, reading_passage_admin, summary_admin, see_detail, password_text, youtube_link
|
1828 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1829 |
HEAD = """
|
1830 |
<meta charset="UTF-8">
|
1831 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
@@ -1893,15 +2015,20 @@ HEAD = """
|
|
1893 |
with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, secondary_hue=gr.themes.colors.amber, text_size = gr.themes.sizes.text_lg), head=HEAD) as demo:
|
1894 |
with gr.Row() as admin:
|
1895 |
password = gr.Textbox(label="Password", type="password", elem_id="password_input", visible=True)
|
1896 |
-
file_upload = gr.File(label="Upload your CSV or Word file", visible=False)
|
1897 |
youtube_link = gr.Textbox(label="Enter YouTube Link", elem_id="youtube_link_input", visible=True)
|
1898 |
video_id = gr.Textbox(label="video_id", visible=False)
|
1899 |
-
|
|
|
1900 |
user_data = gr.Textbox(label="User Data", elem_id="user_data_input", visible=True)
|
1901 |
youtube_link_btn = gr.Button("Submit_YouTube_Link", elem_id="youtube_link_btn", visible=True)
|
|
|
|
|
|
|
|
|
|
|
1902 |
with gr.Tab("AI小精靈"):
|
1903 |
with gr.Row():
|
1904 |
-
with gr.Tab("
|
1905 |
bot_avatar = "https://junyi-avatar.s3.ap-northeast-1.amazonaws.com/live/%20%20foxcat-star-18.png?v=20231113095823614"
|
1906 |
user_avatar = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
|
1907 |
latex_delimiters = [{"left": "$", "right": "$", "display": False}]
|
@@ -1917,28 +2044,33 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
1917 |
btn_create_question = gr.Button("生成其他問題", variant="primary")
|
1918 |
openai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath")
|
1919 |
with gr.Row():
|
1920 |
-
|
1921 |
-
|
1922 |
-
|
1923 |
-
|
1924 |
-
|
1925 |
-
|
1926 |
-
|
1927 |
-
|
1928 |
-
|
1929 |
-
|
1930 |
-
|
1931 |
-
|
1932 |
-
|
1933 |
-
# claude_ai_name = gr.Textbox(label="AI 助理名稱", value="claude3", visible=False)
|
1934 |
-
# claude_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="claude mode chatbot", show_share_button=False, likeable=True)
|
1935 |
-
# claude_msg = gr.Textbox(label="Message")
|
1936 |
-
# claude_send_button = gr.Button("Send", variant="primary")
|
1937 |
with gr.Tab("其他精靈"):
|
1938 |
ai_name = gr.Dropdown(label="選擇 AI 助理", choices=["jutor", "claude3", "groq"], value="jutor")
|
1939 |
-
ai_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="ai_chatbot", show_share_button=False, likeable=True, show_label=False)
|
1940 |
-
|
1941 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1942 |
with gr.Tab("文章模式"):
|
1943 |
with gr.Row() as reading_passage_admin:
|
1944 |
reading_passage_kind = gr.Textbox(value="reading_passage", show_label=False)
|
@@ -1962,6 +2094,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
1962 |
with gr.Tab("關鍵時刻"):
|
1963 |
with gr.Row():
|
1964 |
key_moments_html = gr.HTML(value="")
|
|
|
1965 |
with gr.Tab("教學備課"):
|
1966 |
with gr.Row():
|
1967 |
content_subject = gr.Dropdown(label="選擇主題", choices=["數學", "自然", "國文", "英文", "社會","物理", "化學", "生物", "地理", "歷史", "公民"], value="", visible=False)
|
@@ -2070,7 +2203,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2070 |
mind_map_html = gr.HTML()
|
2071 |
|
2072 |
# --- Event ---
|
2073 |
-
# OPENAI 模式
|
2074 |
send_button.click(
|
2075 |
chat_with_opan_ai_assistant,
|
2076 |
inputs=[password, video_id, thread_id, df_string_output, msg, chatbot, content_subject, content_grade, socratic_mode_btn],
|
@@ -2081,35 +2214,10 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2081 |
inputs=[password, openai_chatbot_audio_input],
|
2082 |
outputs=[msg]
|
2083 |
)
|
2084 |
-
#
|
2085 |
-
|
2086 |
-
|
2087 |
-
|
2088 |
-
# outputs=[groq_msg, groq_chatbot]
|
2089 |
-
# )
|
2090 |
-
# # JUTOR API 模式
|
2091 |
-
# jutor_send_button.click(
|
2092 |
-
# chat_with_ai,
|
2093 |
-
# inputs=[jutor_ai_name, password, video_id, df_string_output, jutor_msg, jutor_chatbot, content_subject, content_grade, socratic_mode_btn],
|
2094 |
-
# outputs=[jutor_msg, jutor_chatbot]
|
2095 |
-
# )
|
2096 |
-
# # CLAUDE 模式
|
2097 |
-
# claude_send_button.click(
|
2098 |
-
# chat_with_ai,
|
2099 |
-
# inputs=[claude_ai_name, password, video_id, df_string_output, claude_msg, claude_chatbot, content_subject, content_grade, socratic_mode_btn],
|
2100 |
-
# outputs=[claude_msg, claude_chatbot]
|
2101 |
-
# )
|
2102 |
-
# ai_chatbot 模式
|
2103 |
-
ai_send_button.click(
|
2104 |
-
chat_with_ai,
|
2105 |
-
inputs=[ai_name, password, video_id, df_string_output, ai_msg, ai_chatbot, content_subject, content_grade, socratic_mode_btn],
|
2106 |
-
outputs=[ai_msg, ai_chatbot]
|
2107 |
-
)
|
2108 |
-
|
2109 |
-
# 连接按钮点击事件
|
2110 |
-
btn_1_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, df_string_output, btn_1, chatbot, content_subject, content_grade, socratic_mode_btn]
|
2111 |
-
btn_2_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, df_string_output, btn_2, chatbot, content_subject, content_grade, socratic_mode_btn]
|
2112 |
-
btn_3_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, df_string_output, btn_3, chatbot, content_subject, content_grade, socratic_mode_btn]
|
2113 |
btn_1.click(
|
2114 |
chat_with_opan_ai_assistant,
|
2115 |
inputs=btn_1_chat_with_opan_ai_assistant_input,
|
@@ -2125,18 +2233,44 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2125 |
inputs=btn_3_chat_with_opan_ai_assistant_input,
|
2126 |
outputs=[msg, chatbot, thread_id]
|
2127 |
)
|
2128 |
-
|
2129 |
btn_create_question.click(
|
2130 |
change_questions,
|
2131 |
inputs = [password, df_string_output],
|
2132 |
outputs = [btn_1, btn_2, btn_3]
|
2133 |
)
|
2134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2135 |
# file_upload.change(process_file, inputs=file_upload, outputs=df_string_output)
|
2136 |
-
file_upload.change(process_file, inputs=file_upload, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
|
2137 |
|
2138 |
# 当输入 YouTube 链接时触发
|
2139 |
-
|
|
|
2140 |
video_id,
|
2141 |
btn_1,
|
2142 |
btn_2,
|
@@ -2154,17 +2288,44 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2154 |
content_subject,
|
2155 |
content_grade,
|
2156 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2157 |
youtube_link.change(
|
2158 |
process_youtube_link,
|
2159 |
-
inputs=
|
2160 |
-
outputs=
|
|
|
|
|
|
|
|
|
2161 |
)
|
2162 |
-
|
2163 |
youtube_link_btn.click(
|
2164 |
process_youtube_link,
|
2165 |
-
inputs=
|
2166 |
-
outputs=
|
|
|
|
|
|
|
|
|
2167 |
)
|
|
|
2168 |
|
2169 |
# 当输入网页链接时触发
|
2170 |
# web_link.change(process_web_link, inputs=web_link, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
|
|
|
489 |
transcript_file_name = f'{video_id}_transcript.json'
|
490 |
transcript_blob_name = f"{video_id}/{transcript_file_name}"
|
491 |
# 检查逐字稿是否存在
|
492 |
+
is_new_transcript = False
|
493 |
is_transcript_exists = GCS_SERVICE.check_file_exists(bucket_name, transcript_blob_name)
|
494 |
if not is_transcript_exists:
|
495 |
# 从YouTube获取逐字稿并上传
|
|
|
508 |
|
509 |
transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
|
510 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, transcript_blob_name, transcript_text)
|
511 |
+
is_new_transcript = True
|
512 |
else:
|
513 |
# 逐字稿已存在,下载逐字稿内容
|
514 |
print("逐字稿已存在于GCS中")
|
|
|
544 |
img_file_id = upload_img_and_get_public_url(gcs_client, bucket_name, screenshot_blob_name, screenshot_path)
|
545 |
entry['img_file_id'] = img_file_id
|
546 |
print(f"截图已上传到GCS: {img_file_id}")
|
547 |
+
is_new_transcript = True
|
548 |
+
|
549 |
+
# 確認是否更新逐字稿文件
|
550 |
+
if is_new_transcript:
|
551 |
+
# 更新逐字稿文件
|
552 |
+
print("===更新逐字稿文件===")
|
553 |
+
print(transcript)
|
554 |
+
print("===更新逐字稿文件===")
|
555 |
+
updated_transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
|
556 |
+
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, transcript_blob_name, updated_transcript_text)
|
557 |
+
print("逐字稿已更新,包括截图链接")
|
558 |
+
updated_transcript_json = json.loads(updated_transcript_text)
|
559 |
+
else:
|
560 |
+
updated_transcript_json = transcript
|
561 |
|
562 |
return updated_transcript_json
|
563 |
|
|
|
698 |
|
699 |
|
700 |
# ---- Web ----
|
701 |
+
# def process_web_link(link):
|
702 |
+
# # 抓取和解析网页内容
|
703 |
+
# response = requests.get(link)
|
704 |
+
# soup = BeautifulSoup(response.content, 'html.parser')
|
705 |
+
# return soup.get_text()
|
706 |
|
707 |
|
708 |
# ---- LLM Generator ----
|
|
|
1773 |
|
1774 |
return run.status
|
1775 |
|
1776 |
+
def streaming_chat_with_open_ai(user_message, chat_history, password, thread_id, trascript, content_subject, content_grade):
|
1777 |
+
verify_password(password)
|
1778 |
+
|
1779 |
+
print("===streaming_chat_with_open_ai===")
|
1780 |
+
print(thread_id)
|
1781 |
+
|
1782 |
+
# 先計算 user_message 是否超過 500 個字
|
1783 |
+
if len(user_message) > 1500:
|
1784 |
+
error_msg = "你的訊息太長了,請縮短訊息長度至五百字以內"
|
1785 |
+
raise gr.Error(error_msg)
|
1786 |
+
|
1787 |
+
# 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
|
1788 |
+
if chat_history is not None and len(chat_history) > 10:
|
1789 |
+
error_msg = "此次對話超過上限"
|
1790 |
+
raise gr.Error(error_msg)
|
1791 |
+
|
1792 |
+
# fake data
|
1793 |
+
socratic_mode = True
|
1794 |
+
|
1795 |
+
try:
|
1796 |
+
assistant_id = "asst_kmvZLNkDUYaNkMNtZEAYxyPq"
|
1797 |
+
client = OPEN_AI_CLIENT
|
1798 |
+
# 直接安排逐字稿資料 in instructions
|
1799 |
+
trascript_json = json.loads(trascript)
|
1800 |
+
# 移除 embed_url, screenshot_path
|
1801 |
+
for entry in trascript_json:
|
1802 |
+
entry.pop('embed_url', None)
|
1803 |
+
entry.pop('screenshot_path', None)
|
1804 |
+
trascript_text = json.dumps(trascript_json, ensure_ascii=False, indent=2)
|
1805 |
+
# trascript_text 移除 \n, 空白
|
1806 |
+
trascript_text = trascript_text.replace("\n", "").replace(" ", "")
|
1807 |
+
|
1808 |
+
instructions = f"""
|
1809 |
+
科目:{content_subject}
|
1810 |
+
年級:{content_grade}
|
1811 |
+
逐字稿資料:{trascript_text}
|
1812 |
+
-------------------------------------
|
1813 |
+
你是一個專業的{content_subject}老師, user 為{content_grade}的學生
|
1814 |
+
socratic_mode = {socratic_mode}
|
1815 |
+
if socratic_mode is True,
|
1816 |
+
- 請用蘇格拉底式的提問方式,引導學生思考,並且給予學生一些提示
|
1817 |
+
- 一次只問一個問題,字數在100字以內
|
1818 |
+
- 不要直接給予答案,讓學生自己思考
|
1819 |
+
- 但可以給予一些提示跟引導,例如給予影片的時間軸,讓學生自己去找答案
|
1820 |
+
|
1821 |
+
if socratic_mode is False,
|
1822 |
+
- 直接回答學生問題,字數在100字以內
|
1823 |
+
|
1824 |
+
rule:
|
1825 |
+
- 請一定要用繁體中文回答 zh-TW,並用台灣人的口語表達,回答時不用特別說明這是台灣人的語氣,也不用說這是「台語的說法」
|
1826 |
+
- 不用提到「逐字稿」這個詞,用「內容」代替
|
1827 |
+
- 如果學生問了一些問題你無法判斷,請告訴學生你無法判斷,並建議學生可以問其他問題
|
1828 |
+
- 或者你可以反問學生一些問題,幫助學生更好的理解資料,字數在100字以內
|
1829 |
+
- 如果學生的問題與資料文本無關,請告訴學生你「無法回答超出影片範圍的問題」,並告訴他可以怎麼問什麼樣的問題(一個就好)
|
1830 |
+
- 只要是參考逐字稿資料,請在回答的最後標註【參考資料:(分):(秒)】
|
1831 |
+
- 回答範圍一定要在逐字稿資料內,不要引用其他資料,請嚴格執行
|
1832 |
+
- 並在重複問句後給予學生鼓勵,讓學生有學習的動力
|
1833 |
+
- 請用 {content_grade} 的學生能懂的方式回答
|
1834 |
+
"""
|
1835 |
+
|
1836 |
+
# 创建线程
|
1837 |
+
if not thread_id:
|
1838 |
+
thread = client.beta.threads.create()
|
1839 |
+
thread_id = thread.id
|
1840 |
+
print(f"new thread_id: {thread_id}")
|
1841 |
+
else:
|
1842 |
+
thread = client.beta.threads.retrieve(thread_id)
|
1843 |
+
print(f"old thread_id: {thread_id}")
|
1844 |
+
|
1845 |
+
# 向线程添加用户的消息
|
1846 |
+
client.beta.threads.messages.create(
|
1847 |
+
thread_id=thread.id,
|
1848 |
+
role="user",
|
1849 |
+
content=user_message + "/n (請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,不用提到「逐字稿」這個詞,用「內容」代替)),請在回答的最後標註【參考資料:(時):(分):(秒)】,(如果是反問學生,就只問一個問題,請幫助學生更好的理解資料,字數在100字以內)"
|
1850 |
+
)
|
1851 |
+
|
1852 |
+
with client.beta.threads.runs.stream(
|
1853 |
+
thread_id=thread.id,
|
1854 |
+
assistant_id=assistant_id,
|
1855 |
+
instructions=instructions,
|
1856 |
+
) as stream:
|
1857 |
+
partial_messages = ""
|
1858 |
+
for event in stream:
|
1859 |
+
if event.data and event.data.object == "thread.message.delta":
|
1860 |
+
message = event.data.delta.content[0].text.value
|
1861 |
+
partial_messages += message
|
1862 |
+
yield partial_messages
|
1863 |
+
|
1864 |
+
except Exception as e:
|
1865 |
+
print(f"Error: {e}")
|
1866 |
+
raise gr.Error(f"Error: {e}")
|
1867 |
+
|
1868 |
+
def create_thread_id():
|
1869 |
+
thread = OPEN_AI_CLIENT.beta.threads.create()
|
1870 |
+
thread_id = thread.id
|
1871 |
+
print(f"create new thread_id: {thread_id}")
|
1872 |
+
return thread_id
|
1873 |
+
|
1874 |
# --- Slide mode ---
|
1875 |
def update_slide(direction):
|
1876 |
global TRANSCRIPTS
|
|
|
1899 |
def next_slide():
|
1900 |
return update_slide(1)
|
1901 |
|
1902 |
+
|
1903 |
+
# --- Init params ---
|
1904 |
def init_params(text, request: gr.Request):
|
1905 |
if request:
|
1906 |
print("Request headers dictionary:", request.headers)
|
|
|
1933 |
|
1934 |
return admin, reading_passage_admin, summary_admin, see_detail, password_text, youtube_link
|
1935 |
|
1936 |
+
def update_state(content_subject, content_grade, trascript, question_1, question_2, question_3):
|
1937 |
+
# inputs=[content_subject, content_grade, df_string_output],
|
1938 |
+
# outputs=[content_subject_state, content_grade_state, trascript_state]
|
1939 |
+
content_subject_state = content_subject
|
1940 |
+
content_grade_state = content_grade
|
1941 |
+
trascript_state = trascript
|
1942 |
+
streaming_chat_thread_id_state = create_thread_id()
|
1943 |
+
ai_chatbot_question_1 = question_1
|
1944 |
+
ai_chatbot_question_2 = question_2
|
1945 |
+
ai_chatbot_question_3 = question_3
|
1946 |
+
|
1947 |
+
return content_subject_state, content_grade_state, trascript_state, streaming_chat_thread_id_state, \
|
1948 |
+
ai_chatbot_question_1, ai_chatbot_question_2, ai_chatbot_question_3
|
1949 |
+
|
1950 |
+
|
1951 |
HEAD = """
|
1952 |
<meta charset="UTF-8">
|
1953 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
2015 |
with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, secondary_hue=gr.themes.colors.amber, text_size = gr.themes.sizes.text_lg), head=HEAD) as demo:
|
2016 |
with gr.Row() as admin:
|
2017 |
password = gr.Textbox(label="Password", type="password", elem_id="password_input", visible=True)
|
|
|
2018 |
youtube_link = gr.Textbox(label="Enter YouTube Link", elem_id="youtube_link_input", visible=True)
|
2019 |
video_id = gr.Textbox(label="video_id", visible=False)
|
2020 |
+
# file_upload = gr.File(label="Upload your CSV or Word file", visible=False)
|
2021 |
+
# web_link = gr.Textbox(label="Enter Web Page Link", visible=False)
|
2022 |
user_data = gr.Textbox(label="User Data", elem_id="user_data_input", visible=True)
|
2023 |
youtube_link_btn = gr.Button("Submit_YouTube_Link", elem_id="youtube_link_btn", visible=True)
|
2024 |
+
with gr.Row() as data_state:
|
2025 |
+
content_subject_state = gr.State() # 使用 gr.State 存储 content_subject
|
2026 |
+
content_grade_state = gr.State() # 使用 gr.State 存储 content_grade
|
2027 |
+
trascript_state = gr.State() # 使用 gr.State 存储 trascript
|
2028 |
+
streaming_chat_thread_id_state = gr.State() # 使用 gr.State 存储 streaming_chat_thread_id
|
2029 |
with gr.Tab("AI小精靈"):
|
2030 |
with gr.Row():
|
2031 |
+
with gr.Tab("飛特精靈"):
|
2032 |
bot_avatar = "https://junyi-avatar.s3.ap-northeast-1.amazonaws.com/live/%20%20foxcat-star-18.png?v=20231113095823614"
|
2033 |
user_avatar = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
|
2034 |
latex_delimiters = [{"left": "$", "right": "$", "display": False}]
|
|
|
2044 |
btn_create_question = gr.Button("生成其他問題", variant="primary")
|
2045 |
openai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath")
|
2046 |
with gr.Row():
|
2047 |
+
msg = gr.Textbox(label="訊息",scale=3)
|
2048 |
+
send_button = gr.Button("送出", variant="primary", scale=1)
|
2049 |
+
with gr.Tab("飛特音速"):
|
2050 |
+
additional_inputs = [password, streaming_chat_thread_id_state, trascript_state, content_subject_state, content_grade_state]
|
2051 |
+
streaming_chat = gr.ChatInterface(
|
2052 |
+
fn=streaming_chat_with_open_ai,
|
2053 |
+
additional_inputs=additional_inputs,
|
2054 |
+
submit_btn="送出",
|
2055 |
+
retry_btn=None,
|
2056 |
+
undo_btn="⏪ 上一步",
|
2057 |
+
clear_btn="🗑️ 清除全部",
|
2058 |
+
stop_btn="🛑 停止",
|
2059 |
+
)
|
|
|
|
|
|
|
|
|
2060 |
with gr.Tab("其他精靈"):
|
2061 |
ai_name = gr.Dropdown(label="選擇 AI 助理", choices=["jutor", "claude3", "groq"], value="jutor")
|
2062 |
+
ai_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="ai_chatbot", show_share_button=False, likeable=True, show_label=False, latex_delimiters=latex_delimiters)
|
2063 |
+
ai_chatbot_socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
|
2064 |
+
with gr.Row():
|
2065 |
+
with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion_2:
|
2066 |
+
ai_chatbot_question_1 = gr.Button("問題一")
|
2067 |
+
ai_chatbot_question_2 = gr.Button("問題一")
|
2068 |
+
ai_chatbot_question_3 = gr.Button("問題一")
|
2069 |
+
ai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath")
|
2070 |
+
with gr.Row():
|
2071 |
+
ai_msg = gr.Textbox(label="Message",scale=3)
|
2072 |
+
ai_send_button = gr.Button("Send", variant="primary",scale=1)
|
2073 |
+
|
2074 |
with gr.Tab("文章模式"):
|
2075 |
with gr.Row() as reading_passage_admin:
|
2076 |
reading_passage_kind = gr.Textbox(value="reading_passage", show_label=False)
|
|
|
2094 |
with gr.Tab("關鍵時刻"):
|
2095 |
with gr.Row():
|
2096 |
key_moments_html = gr.HTML(value="")
|
2097 |
+
|
2098 |
with gr.Tab("教學備課"):
|
2099 |
with gr.Row():
|
2100 |
content_subject = gr.Dropdown(label="選擇主題", choices=["數學", "自然", "國文", "英文", "社會","物理", "化學", "生物", "地理", "歷史", "公民"], value="", visible=False)
|
|
|
2203 |
mind_map_html = gr.HTML()
|
2204 |
|
2205 |
# --- Event ---
|
2206 |
+
# OPENAI ASSISTANT CHATBOT 模式
|
2207 |
send_button.click(
|
2208 |
chat_with_opan_ai_assistant,
|
2209 |
inputs=[password, video_id, thread_id, df_string_output, msg, chatbot, content_subject, content_grade, socratic_mode_btn],
|
|
|
2214 |
inputs=[password, openai_chatbot_audio_input],
|
2215 |
outputs=[msg]
|
2216 |
)
|
2217 |
+
# OPENAI ASSISTANT CHATBOT 連接按鈕點擊事件
|
2218 |
+
btn_1_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, df_string_output, btn_1, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2219 |
+
btn_2_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, df_string_output, btn_2, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2220 |
+
btn_3_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, df_string_output, btn_3, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2221 |
btn_1.click(
|
2222 |
chat_with_opan_ai_assistant,
|
2223 |
inputs=btn_1_chat_with_opan_ai_assistant_input,
|
|
|
2233 |
inputs=btn_3_chat_with_opan_ai_assistant_input,
|
2234 |
outputs=[msg, chatbot, thread_id]
|
2235 |
)
|
|
|
2236 |
btn_create_question.click(
|
2237 |
change_questions,
|
2238 |
inputs = [password, df_string_output],
|
2239 |
outputs = [btn_1, btn_2, btn_3]
|
2240 |
)
|
2241 |
|
2242 |
+
# ai_chatbot 模式
|
2243 |
+
ai_send_button.click(
|
2244 |
+
chat_with_ai,
|
2245 |
+
inputs=[ai_name, password, video_id, df_string_output, ai_msg, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn],
|
2246 |
+
outputs=[ai_msg, ai_chatbot]
|
2247 |
+
)
|
2248 |
+
# ai_chatbot 连接按钮点击事件
|
2249 |
+
ai_chatbot_question_1_chat_with_ai_input =[ai_name, password, video_id, df_string_output, ai_chatbot_question_1, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2250 |
+
ai_chatbot_question_2_chat_with_ai_input =[ai_name, password, video_id, df_string_output, ai_chatbot_question_2, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2251 |
+
ai_chatbot_question_3_chat_with_ai_input =[ai_name, password, video_id, df_string_output, ai_chatbot_question_3, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2252 |
+
ai_chatbot_question_1.click(
|
2253 |
+
chat_with_ai,
|
2254 |
+
inputs=ai_chatbot_question_1_chat_with_ai_input,
|
2255 |
+
outputs=[ai_msg, ai_chatbot]
|
2256 |
+
)
|
2257 |
+
ai_chatbot_question_2.click(
|
2258 |
+
chat_with_ai,
|
2259 |
+
inputs=ai_chatbot_question_2_chat_with_ai_input,
|
2260 |
+
outputs=[ai_msg, ai_chatbot]
|
2261 |
+
)
|
2262 |
+
ai_chatbot_question_3.click(
|
2263 |
+
chat_with_ai,
|
2264 |
+
inputs=ai_chatbot_question_3_chat_with_ai_input,
|
2265 |
+
outputs=[ai_msg, ai_chatbot]
|
2266 |
+
)
|
2267 |
+
|
2268 |
# file_upload.change(process_file, inputs=file_upload, outputs=df_string_output)
|
2269 |
+
# file_upload.change(process_file, inputs=file_upload, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
|
2270 |
|
2271 |
# 当输入 YouTube 链接时触发
|
2272 |
+
process_youtube_link_inputs = [password, youtube_link]
|
2273 |
+
process_youtube_link_outputs = [
|
2274 |
video_id,
|
2275 |
btn_1,
|
2276 |
btn_2,
|
|
|
2288 |
content_subject,
|
2289 |
content_grade,
|
2290 |
]
|
2291 |
+
update_state_inputs = [
|
2292 |
+
content_subject,
|
2293 |
+
content_grade,
|
2294 |
+
df_string_output,
|
2295 |
+
btn_1,
|
2296 |
+
btn_2,
|
2297 |
+
btn_3
|
2298 |
+
]
|
2299 |
+
update_state_outputs = [
|
2300 |
+
content_subject_state,
|
2301 |
+
content_grade_state,
|
2302 |
+
trascript_state,
|
2303 |
+
streaming_chat_thread_id_state,
|
2304 |
+
ai_chatbot_question_1,
|
2305 |
+
ai_chatbot_question_2,
|
2306 |
+
ai_chatbot_question_3
|
2307 |
+
]
|
2308 |
+
|
2309 |
youtube_link.change(
|
2310 |
process_youtube_link,
|
2311 |
+
inputs=process_youtube_link_inputs,
|
2312 |
+
outputs=process_youtube_link_outputs
|
2313 |
+
).then(
|
2314 |
+
update_state,
|
2315 |
+
inputs=update_state_inputs,
|
2316 |
+
outputs=update_state_outputs
|
2317 |
)
|
2318 |
+
|
2319 |
youtube_link_btn.click(
|
2320 |
process_youtube_link,
|
2321 |
+
inputs=process_youtube_link_inputs,
|
2322 |
+
outputs=process_youtube_link_outputs
|
2323 |
+
).then(
|
2324 |
+
update_state,
|
2325 |
+
inputs=update_state_inputs,
|
2326 |
+
outputs=update_state_outputs
|
2327 |
)
|
2328 |
+
|
2329 |
|
2330 |
# 当输入网页链接时触发
|
2331 |
# web_link.change(process_web_link, inputs=web_link, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
|
chatbot.py
CHANGED
@@ -122,7 +122,7 @@ class Chatbot:
|
|
122 |
request_payload = {
|
123 |
"model": "mixtral-8x7b-32768",
|
124 |
"messages": messages,
|
125 |
-
"max_tokens":
|
126 |
}
|
127 |
groq_client = self.ai_client
|
128 |
response = groq_client.chat.completions.create(**request_payload)
|
@@ -141,7 +141,7 @@ class Chatbot:
|
|
141 |
"accept": "application/json",
|
142 |
"body": json.dumps({
|
143 |
"anthropic_version": "bedrock-2023-05-31",
|
144 |
-
"max_tokens":
|
145 |
"system": system_prompt,
|
146 |
"messages": messages
|
147 |
})
|
|
|
122 |
request_payload = {
|
123 |
"model": "mixtral-8x7b-32768",
|
124 |
"messages": messages,
|
125 |
+
"max_tokens": 500 # 設定一個較大的值,可根據需要調整
|
126 |
}
|
127 |
groq_client = self.ai_client
|
128 |
response = groq_client.chat.completions.create(**request_payload)
|
|
|
141 |
"accept": "application/json",
|
142 |
"body": json.dumps({
|
143 |
"anthropic_version": "bedrock-2023-05-31",
|
144 |
+
"max_tokens": 500,
|
145 |
"system": system_prompt,
|
146 |
"messages": messages
|
147 |
})
|
requirements.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
gradio
|
2 |
pandas
|
3 |
-
openai>=1.
|
4 |
requests
|
5 |
beautifulsoup4
|
6 |
python-docx
|
|
|
1 |
+
gradio==4.8.0
|
2 |
pandas
|
3 |
+
openai>=1.16.2
|
4 |
requests
|
5 |
beautifulsoup4
|
6 |
python-docx
|