Spaces:
Sleeping
Sleeping
with gr.Tab("飛特音速版"):
Browse files- app.py +170 -7
- requirements.txt +2 -2
app.py
CHANGED
@@ -635,6 +635,7 @@ def process_youtube_link(password, link):
|
|
635 |
formatted_transcript_json, \
|
636 |
summary, \
|
637 |
key_moments_html, \
|
|
|
638 |
mind_map, \
|
639 |
mind_map_html, \
|
640 |
html_content, \
|
@@ -1766,6 +1767,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 +1893,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 +1927,16 @@ 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">
|
@@ -1890,6 +2001,17 @@ HEAD = """
|
|
1890 |
</script>
|
1891 |
"""
|
1892 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
@@ -1899,9 +2021,14 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
1899 |
web_link = gr.Textbox(label="Enter Web Page Link", visible=False)
|
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}]
|
@@ -1939,6 +2066,17 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
1939 |
ai_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="ai_chatbot", show_share_button=False, likeable=True, show_label=False)
|
1940 |
ai_msg = gr.Textbox(label="Message")
|
1941 |
ai_send_button = gr.Button("Send", variant="primary")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 +2100,17 @@ 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)
|
@@ -2136,7 +2285,8 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
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,
|
@@ -2144,6 +2294,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2144 |
df_string_output,
|
2145 |
df_summarise,
|
2146 |
key_moments_html,
|
|
|
2147 |
mind_map,
|
2148 |
mind_map_html,
|
2149 |
transcript_html,
|
@@ -2154,17 +2305,29 @@ 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])
|
|
|
635 |
formatted_transcript_json, \
|
636 |
summary, \
|
637 |
key_moments_html, \
|
638 |
+
key_moments, \
|
639 |
mind_map, \
|
640 |
mind_map_html, \
|
641 |
html_content, \
|
|
|
1767 |
|
1768 |
return run.status
|
1769 |
|
1770 |
+
def streaming_chat_with_open_ai(user_message, chat_history, password, thread_id, trascript, content_subject, content_grade):
|
1771 |
+
verify_password(password)
|
1772 |
+
|
1773 |
+
print("===streaming_chat_with_open_ai===")
|
1774 |
+
print(thread_id)
|
1775 |
+
|
1776 |
+
# 先計算 user_message 是否超過 500 個字
|
1777 |
+
if len(user_message) > 1500:
|
1778 |
+
error_msg = "你的訊息太長了,請縮短訊息長度至五百字以內"
|
1779 |
+
raise gr.Error(error_msg)
|
1780 |
+
|
1781 |
+
# 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
|
1782 |
+
if chat_history is not None and len(chat_history) > 10:
|
1783 |
+
error_msg = "此次對話超過上限"
|
1784 |
+
raise gr.Error(error_msg)
|
1785 |
+
|
1786 |
+
# fake data
|
1787 |
+
socratic_mode = True
|
1788 |
+
|
1789 |
+
try:
|
1790 |
+
assistant_id = "asst_kmvZLNkDUYaNkMNtZEAYxyPq"
|
1791 |
+
client = OPEN_AI_CLIENT
|
1792 |
+
# 直接安排逐字稿資料 in instructions
|
1793 |
+
trascript_json = json.loads(trascript)
|
1794 |
+
# 移除 embed_url, screenshot_path
|
1795 |
+
for entry in trascript_json:
|
1796 |
+
entry.pop('embed_url', None)
|
1797 |
+
entry.pop('screenshot_path', None)
|
1798 |
+
trascript_text = json.dumps(trascript_json, ensure_ascii=False, indent=2)
|
1799 |
+
# trascript_text 移除 \n, 空白
|
1800 |
+
trascript_text = trascript_text.replace("\n", "").replace(" ", "")
|
1801 |
+
|
1802 |
+
instructions = f"""
|
1803 |
+
科目:{content_subject}
|
1804 |
+
年級:{content_grade}
|
1805 |
+
逐字稿資料:{trascript_text}
|
1806 |
+
-------------------------------------
|
1807 |
+
你是一個專業的{content_subject}老師, user 為{content_grade}的學生
|
1808 |
+
socratic_mode = {socratic_mode}
|
1809 |
+
if socratic_mode is True,
|
1810 |
+
- 請用蘇格拉底式的提問方式,引導學生思考,並且給予學生一些提示
|
1811 |
+
- 一次只問一個問題,字數在100字以內
|
1812 |
+
- 不要直接給予答案,讓學生自己思考
|
1813 |
+
- 但可以給予一些提示跟引導,例如給予影片的時間軸,讓學生自己去找答案
|
1814 |
+
|
1815 |
+
if socratic_mode is False,
|
1816 |
+
- 直接回答學生問題,字數在100字以內
|
1817 |
+
|
1818 |
+
rule:
|
1819 |
+
- 請一定要用繁體中文回答 zh-TW,並用台灣人的口語表達,回答時不用特別說明這是台灣人的語氣,也不用說這是「台語的說法」
|
1820 |
+
- 不用提到「逐字稿」這個詞,用「內容」代替
|
1821 |
+
- 如果學生問了一些問題你無法判斷,請告訴學生你無法判斷,並建議學生可以問其他問題
|
1822 |
+
- 或者你可以反問學生一些問題,幫助學生更好的理解資料,字數在100字以內
|
1823 |
+
- 如果學生的問題與資料文本無關,請告訴學生你「無法回答超出影片範圍的問題」,並告訴他可以怎麼問什麼樣的問題(一個就好)
|
1824 |
+
- 只要是參考逐字稿資料,請在回答的最後標註【參考資料:(分):(秒)】
|
1825 |
+
- 回答範圍一定要在逐字稿資料內,不要引用其他資料,請嚴格執行
|
1826 |
+
- 並在重複問句後給予學生鼓勵,讓學生有學習的動力
|
1827 |
+
- 請用 {content_grade} 的學生能懂的方式回答
|
1828 |
+
"""
|
1829 |
+
|
1830 |
+
# 创建线程
|
1831 |
+
if not thread_id:
|
1832 |
+
thread = client.beta.threads.create()
|
1833 |
+
thread_id = thread.id
|
1834 |
+
print(f"new thread_id: {thread_id}")
|
1835 |
+
else:
|
1836 |
+
thread = client.beta.threads.retrieve(thread_id)
|
1837 |
+
print(f"old thread_id: {thread_id}")
|
1838 |
+
|
1839 |
+
# 向线程添加用户的消息
|
1840 |
+
client.beta.threads.messages.create(
|
1841 |
+
thread_id=thread.id,
|
1842 |
+
role="user",
|
1843 |
+
content=user_message + "/n (請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,不用提到「逐字稿」這個詞,用「內容」代替)),請在回答的最後標註【參考資料:(時):(分):(秒)】,(如果是反問學生,就只問一個問題,請幫助學生更好的理解資料,字數在100字以內)"
|
1844 |
+
)
|
1845 |
+
|
1846 |
+
with client.beta.threads.runs.stream(
|
1847 |
+
thread_id=thread.id,
|
1848 |
+
assistant_id=assistant_id,
|
1849 |
+
instructions=instructions,
|
1850 |
+
) as stream:
|
1851 |
+
partial_messages = ""
|
1852 |
+
for event in stream:
|
1853 |
+
if event.data and event.data.object == "thread.message.delta":
|
1854 |
+
message = event.data.delta.content[0].text.value
|
1855 |
+
partial_messages += message
|
1856 |
+
yield partial_messages
|
1857 |
+
|
1858 |
+
except Exception as e:
|
1859 |
+
print(f"Error: {e}")
|
1860 |
+
raise gr.Error(f"Error: {e}")
|
1861 |
+
|
1862 |
+
def create_thread_id():
|
1863 |
+
thread = OPEN_AI_CLIENT.beta.threads.create()
|
1864 |
+
thread_id = thread.id
|
1865 |
+
print(f"create new thread_id: {thread_id}")
|
1866 |
+
return thread_id
|
1867 |
+
|
1868 |
# --- Slide mode ---
|
1869 |
def update_slide(direction):
|
1870 |
global TRANSCRIPTS
|
|
|
1893 |
def next_slide():
|
1894 |
return update_slide(1)
|
1895 |
|
1896 |
+
|
1897 |
+
# --- Init params ---
|
1898 |
def init_params(text, request: gr.Request):
|
1899 |
if request:
|
1900 |
print("Request headers dictionary:", request.headers)
|
|
|
1927 |
|
1928 |
return admin, reading_passage_admin, summary_admin, see_detail, password_text, youtube_link
|
1929 |
|
1930 |
+
def update_state(content_subject, content_grade, trascript):
|
1931 |
+
# inputs=[content_subject, content_grade, df_string_output],
|
1932 |
+
# outputs=[content_subject_state, content_grade_state, trascript_state]
|
1933 |
+
content_subject_state = content_subject
|
1934 |
+
content_grade_state = content_grade
|
1935 |
+
trascript_state = trascript
|
1936 |
+
streaming_chat_thread_id_state = create_thread_id()
|
1937 |
+
|
1938 |
+
return content_subject_state, content_grade_state, trascript_state, streaming_chat_thread_id_state
|
1939 |
+
|
1940 |
HEAD = """
|
1941 |
<meta charset="UTF-8">
|
1942 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
2001 |
</script>
|
2002 |
"""
|
2003 |
|
2004 |
+
def update_key_moments_section(key_moments):
|
2005 |
+
blocks = []
|
2006 |
+
for moment in key_moments:
|
2007 |
+
with gr.Row() as row:
|
2008 |
+
with gr.Column(scale=1):
|
2009 |
+
gr.Gallery(images=moment['images'], show_label=False)
|
2010 |
+
with gr.Column(scale=2):
|
2011 |
+
gr.Textbox(value=moment['text'], show_label=False, interactive=False)
|
2012 |
+
blocks.append(row)
|
2013 |
+
return blocks
|
2014 |
+
|
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)
|
|
|
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}]
|
|
|
2066 |
ai_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="ai_chatbot", show_share_button=False, likeable=True, show_label=False)
|
2067 |
ai_msg = gr.Textbox(label="Message")
|
2068 |
ai_send_button = gr.Button("Send", variant="primary")
|
2069 |
+
with gr.Tab("飛特音速版"):
|
2070 |
+
additional_inputs = [password, streaming_chat_thread_id_state, trascript_state, content_subject_state, content_grade_state]
|
2071 |
+
streaming_chat = gr.ChatInterface(
|
2072 |
+
fn=streaming_chat_with_open_ai,
|
2073 |
+
additional_inputs=additional_inputs,
|
2074 |
+
submit_btn="送出",
|
2075 |
+
retry_btn="🔄 重新回答",
|
2076 |
+
undo_btn="⏪ 上一步",
|
2077 |
+
clear_btn="🗑️ 清除全部",
|
2078 |
+
stop_btn="🛑 停止",
|
2079 |
+
)
|
2080 |
with gr.Tab("文章模式"):
|
2081 |
with gr.Row() as reading_passage_admin:
|
2082 |
reading_passage_kind = gr.Textbox(value="reading_passage", show_label=False)
|
|
|
2100 |
with gr.Tab("關鍵時刻"):
|
2101 |
with gr.Row():
|
2102 |
key_moments_html = gr.HTML(value="")
|
2103 |
+
with gr.Tab("Mind Map"):
|
2104 |
+
key_moments_state = gr.State()
|
2105 |
+
update_key_moments_button = gr.Button("更新關鍵時刻")
|
2106 |
+
key_moments_section = gr.Blocks()
|
2107 |
+
|
2108 |
+
update_key_moments_button.click(
|
2109 |
+
fn=update_key_moments_section,
|
2110 |
+
inputs=[],
|
2111 |
+
outputs=key_moments_section
|
2112 |
+
)
|
2113 |
+
|
2114 |
with gr.Tab("教學備課"):
|
2115 |
with gr.Row():
|
2116 |
content_subject = gr.Dropdown(label="選擇主題", choices=["數學", "自然", "國文", "英文", "社會","物理", "化學", "生物", "地理", "歷史", "公民"], value="", visible=False)
|
|
|
2285 |
file_upload.change(process_file, inputs=file_upload, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
|
2286 |
|
2287 |
# 当输入 YouTube 链接时触发
|
2288 |
+
process_youtube_link_inputs = [password, youtube_link]
|
2289 |
+
process_youtube_link_outputs = [
|
2290 |
video_id,
|
2291 |
btn_1,
|
2292 |
btn_2,
|
|
|
2294 |
df_string_output,
|
2295 |
df_summarise,
|
2296 |
key_moments_html,
|
2297 |
+
key_moments_state,
|
2298 |
mind_map,
|
2299 |
mind_map_html,
|
2300 |
transcript_html,
|
|
|
2305 |
content_subject,
|
2306 |
content_grade,
|
2307 |
]
|
2308 |
+
update_state_inputs = [content_subject, content_grade, df_string_output]
|
2309 |
+
update_state_outputs = [content_subject_state, content_grade_state, trascript_state, streaming_chat_thread_id_state]
|
2310 |
+
|
2311 |
youtube_link.change(
|
2312 |
process_youtube_link,
|
2313 |
+
inputs=process_youtube_link_inputs,
|
2314 |
+
outputs=process_youtube_link_outputs
|
2315 |
+
).then(
|
2316 |
+
update_state,
|
2317 |
+
inputs=update_state_inputs,
|
2318 |
+
outputs=update_state_outputs
|
2319 |
)
|
2320 |
+
|
2321 |
youtube_link_btn.click(
|
2322 |
process_youtube_link,
|
2323 |
+
inputs=process_youtube_link_inputs,
|
2324 |
+
outputs=process_youtube_link_outputs
|
2325 |
+
).then(
|
2326 |
+
update_state,
|
2327 |
+
inputs=update_state_inputs,
|
2328 |
+
outputs=update_state_outputs
|
2329 |
)
|
2330 |
+
|
2331 |
|
2332 |
# 当输入网页链接时触发
|
2333 |
# web_link.change(process_web_link, inputs=web_link, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
|
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
|