youngtsai commited on
Commit
0c0734c
·
1 Parent(s): cac57ce

with gr.Tab("飛特音速版"):

Browse files
Files changed (2) hide show
  1. app.py +170 -7
  2. 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
- process_youtube_link_output = [
 
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=[password,youtube_link],
2160
- outputs=process_youtube_link_output
 
 
 
 
2161
  )
2162
-
2163
  youtube_link_btn.click(
2164
  process_youtube_link,
2165
- inputs=[password, youtube_link],
2166
- outputs=process_youtube_link_output
 
 
 
 
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>=4.24.0
2
  pandas
3
- openai>=1.0.0
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