youngtsai commited on
Commit
901bdeb
1 Parent(s): 7935a4b
Files changed (3) hide show
  1. app.py +360 -134
  2. chatbot.py +1 -1
  3. educational_material.py +19 -17
app.py CHANGED
@@ -367,7 +367,7 @@ def extract_youtube_id(url):
367
  else:
368
  return None
369
 
370
- def get_transcript(video_id):
371
  languages = ['zh-TW', 'zh-Hant', 'zh', 'en'] # 優先順序列表
372
  for language in languages:
373
  try:
@@ -380,7 +380,7 @@ def get_transcript(video_id):
380
  continue # 當前語言的字幕沒有找到,繼續嘗試下一個語言
381
  return None # 所有嘗試都失敗,返回None
382
 
383
- def generate_transcription(video_id):
384
  youtube_url = f'https://www.youtube.com/watch?v={video_id}'
385
  codec_name = "mp3"
386
  outtmpl = f"{OUTPUT_PATH}/{video_id}.%(ext)s"
@@ -457,17 +457,17 @@ def process_transcript_and_screenshots_on_gcs(video_id):
457
  print("逐字稿文件不存在于GCS中,重新建立")
458
  # 从YouTube获取逐字稿并上传
459
  try:
460
- transcript = get_transcript(video_id)
461
  except:
462
  # call open ai whisper
463
  print("===call open ai whisper===")
464
- transcript = generate_transcription(video_id)
465
 
466
  if transcript:
467
  print("成功獲取字幕")
468
  else:
469
  print("沒有找到字幕")
470
- transcript = generate_transcription(video_id)
471
 
472
  transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
473
  upload_file_to_gcs_with_json_string(gcs_client, bucket_name, transcript_blob_name, transcript_text)
@@ -572,6 +572,7 @@ def process_youtube_link(password, link):
572
  questions = get_questions(video_id, formatted_simple_transcript, source)
573
  questions_json = json.dumps(questions, ensure_ascii=False, indent=2)
574
  summary_json = get_video_id_summary(video_id, formatted_simple_transcript, source)
 
575
  summary = summary_json["summary"]
576
  key_moments_json = get_key_moments(video_id, formatted_simple_transcript, formatted_transcript, source)
577
  key_moments = key_moments_json["key_moments"]
@@ -586,6 +587,7 @@ def process_youtube_link(password, link):
586
  mind_map = mind_map_json["mind_map"]
587
  mind_map_html = get_mind_map_html(mind_map)
588
  reading_passage_json = get_reading_passage(video_id, formatted_simple_transcript, source)
 
589
  reading_passage = reading_passage_json["reading_passage"]
590
  meta_data = get_meta_data(video_id)
591
  subject = meta_data["subject"]
@@ -598,6 +600,7 @@ def process_youtube_link(password, link):
598
  questions[1] if len(questions) > 1 else "", \
599
  questions[2] if len(questions) > 2 else "", \
600
  original_transcript, \
 
601
  summary, \
602
  key_moments_text, \
603
  key_moments_html, \
@@ -607,6 +610,7 @@ def process_youtube_link(password, link):
607
  simple_html_content, \
608
  first_image, \
609
  first_text, \
 
610
  reading_passage, \
611
  subject, \
612
  grade
@@ -750,6 +754,8 @@ def generate_reading_passage(df_string):
750
  請一定要使用繁體中文 zh-TW,並用台灣人的口語
751
  產生的結果不要前後文解釋,也不要敘述這篇文章怎麼產生的
752
  只需要專注提供 Reading Passage,字數在 500 字以內
 
 
753
  """
754
  messages = [
755
  {"role": "system", "content": sys_content},
@@ -757,7 +763,7 @@ def generate_reading_passage(df_string):
757
  ]
758
 
759
  request_payload = {
760
- "model": "gpt-4-1106-preview",
761
  "messages": messages,
762
  "max_tokens": 4000,
763
  }
@@ -834,7 +840,7 @@ def generate_mind_map(df_string):
834
  ]
835
 
836
  request_payload = {
837
- "model": "gpt-4-1106-preview",
838
  "messages": messages,
839
  "max_tokens": 4000,
840
  }
@@ -916,22 +922,27 @@ def generate_summarise(df_string):
916
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
917
  user_content = f"""
918
  請根據 {df_string},判斷這份文本
919
- 如果是資料類型,請提估欄位敘述、資料樣態與資料分析,告訴學生這張表的意義,以及可能的結論與對應方式
920
-
921
- 如果是影片類型,請提估影片內容,告訴學生這部影片的意義,
922
  整體摘要在一百字以內
923
- 小範圍切出不同段落的相對應時間軸的重點摘要,最多不超過五段
924
- 注意不要遺漏任何一段時間軸的內容
925
- 格式為 【start - end】: 摘要
926
  以及可能的結論與結尾延伸小問題提供學生作反思
 
 
927
 
928
  整體格式為:
929
- 🗂️ 1. 內容類型:?
930
- 📚 2. 整體摘要
931
- 🔖 3. 重點概念
932
- 🔑 4. 關鍵時刻
933
- 💡 5. 為什麼我們要學這個?
934
- 6. 延伸小問題
 
 
 
 
 
 
 
935
  """
936
 
937
  # 🗂️ 1. 內容類型:?
@@ -947,7 +958,7 @@ def generate_summarise(df_string):
947
  ]
948
 
949
  request_payload = {
950
- "model": "gpt-4-turbo-preview",
951
  "messages": messages,
952
  "max_tokens": 4000,
953
  }
@@ -1014,9 +1025,17 @@ def get_questions(video_id, df_string, source="gcs"):
1014
 
1015
  def generate_questions(df_string):
1016
  # 使用 OpenAI 生成基于上传数据的问题
 
 
 
 
 
 
 
 
1017
 
1018
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,並用既有資料為本質猜測用戶可能會問的問題,使用 zh-TW"
1019
- user_content = f"請根據 {df_string} 生成三個問題,並用 JSON 格式返回 questions:[q1的敘述text, q2的敘述text, q3的敘述text]"
1020
  messages = [
1021
  {"role": "system", "content": sys_content},
1022
  {"role": "user", "content": user_content}
@@ -1029,7 +1048,7 @@ def generate_questions(df_string):
1029
 
1030
 
1031
  request_payload = {
1032
- "model": "gpt-4-1106-preview",
1033
  "messages": messages,
1034
  "max_tokens": 4000,
1035
  "response_format": response_format
@@ -1147,7 +1166,7 @@ def generate_key_moments(formatted_simple_transcript, formatted_transcript):
1147
  response_format = { "type": "json_object" }
1148
 
1149
  request_payload = {
1150
- "model": "gpt-4-1106-preview",
1151
  "messages": messages,
1152
  "max_tokens": 4096,
1153
  "response_format": response_format
@@ -1189,7 +1208,7 @@ def generate_key_moments_keywords(transcript):
1189
  {"role": "user", "content": user_content}
1190
  ]
1191
  request_payload = {
1192
- "model": "gpt-4-1106-preview",
1193
  "messages": messages,
1194
  "max_tokens": 100,
1195
  }
@@ -1402,6 +1421,27 @@ def get_key_moments_html(key_moments):
1402
  return key_moments_html
1403
 
1404
  # ---- LLM CRUD ----
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1405
  def enable_edit_mode():
1406
  return gr.update(interactive=True)
1407
 
@@ -1426,15 +1466,17 @@ def update_LLM_content(video_id, new_content, kind):
1426
  blob_name = f"{video_id}/{file_name}"
1427
 
1428
  if kind == "reading_passage":
 
 
1429
  reading_passage_json = {"reading_passage": str(new_content)}
1430
  reading_passage_text = json.dumps(reading_passage_json, ensure_ascii=False, indent=2)
1431
  upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, reading_passage_text)
1432
- updated_content = reading_passage_text
1433
  elif kind == "summary":
1434
  summary_json = {"summary": str(new_content)}
1435
  summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
1436
  upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, summary_text)
1437
- updated_content = summary_text
1438
  elif kind == "mind_map":
1439
  mind_map_json = {"mind_map": str(new_content)}
1440
  mind_map_text = json.dumps(mind_map_json, ensure_ascii=False, indent=2)
@@ -1507,7 +1549,6 @@ def create_LLM_content(video_id, df_string, kind):
1507
 
1508
  return gr.update(value=content, interactive=False)
1509
 
1510
-
1511
  # AI 生成教學素材
1512
  def get_meta_data(video_id, source="gcs"):
1513
  if source == "gcs":
@@ -1611,7 +1652,7 @@ def generate_ai_content(password, df_string, topic, grade, level, specific_featu
1611
  prompt = material.generate_content_prompt()
1612
  user_content = material.build_user_content()
1613
  messages = material.build_messages(user_content)
1614
- ai_model_name = "gpt-4-1106-preview"
1615
  request_payload = {
1616
  "model": ai_model_name,
1617
  "messages": messages,
@@ -1625,7 +1666,7 @@ def generate_exam_fine_tune_result(password, exam_result_prompt , df_string_outp
1625
  material = EducationalMaterial(df_string_output, "", "", "", "", "")
1626
  user_content = material.build_fine_tune_user_content(exam_result_prompt, exam_result, exam_result_fine_tune_prompt)
1627
  messages = material.build_messages(user_content)
1628
- ai_model_name = "gpt-4-1106-preview"
1629
  request_payload = {
1630
  "model": ai_model_name,
1631
  "messages": messages,
@@ -1661,7 +1702,7 @@ def get_instructions(content_subject, content_grade, key_moments):
1661
  Language: Traditional Chinese ZH-TW (it's very important), suitable for {content_grade} th-grade level.
1662
  Response:
1663
  - Single question, under 100 characters
1664
- - include math symbols (use LaTeX $ to cover before and after)
1665
  - hint with video timestamp which format 【參考:00:00:00】.
1666
  - Sometimes encourage user by Taiwanese style with relaxing atmosphere.
1667
  - if user ask questions not include in context,
@@ -1670,19 +1711,28 @@ def get_instructions(content_subject, content_grade, key_moments):
1670
  """
1671
  return instructions
1672
 
1673
- def chat_with_ai(ai_name, password, video_id, trascript_state, key_moments, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
1674
  verify_password(password)
1675
 
1676
- if chat_history is not None and len(chat_history) > 10:
1677
- error_msg = "此次對話超過上限"
 
 
 
1678
  raise gr.Error(error_msg)
1679
 
 
 
 
1680
  if ai_name == "jutor":
1681
  ai_client = ""
1682
  elif ai_name == "claude3":
1683
  ai_client = BEDROCK_CLIENT
1684
  elif ai_name == "groq":
1685
  ai_client = GROQ_CLIENT
 
 
 
1686
  if isinstance(trascript_state, str):
1687
  simple_transcript = json.loads(trascript_state)
1688
  else:
@@ -1696,7 +1746,7 @@ def chat_with_ai(ai_name, password, video_id, trascript_state, key_moments, user
1696
  for moment in key_moments_json:
1697
  moment.pop('images', None)
1698
  moment.pop('end', None)
1699
- moment.pop('text', None)
1700
  key_moments_text = json.dumps(key_moments_json, ensure_ascii=False)
1701
 
1702
  instructions = get_instructions(content_subject, content_grade, key_moments_text)
@@ -1735,9 +1785,12 @@ def chat_with_ai(ai_name, password, video_id, trascript_state, key_moments, user
1735
  print(f"Error: {e}")
1736
  return "请求失败,请稍后再试!", chat_history
1737
 
1738
- def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript_state, key_moments, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
1739
  verify_password(password)
1740
 
 
 
 
1741
  # 先計算 user_message 是否超過 500 個字
1742
  if len(user_message) > 1500:
1743
  error_msg = "你的訊息太長了,請縮短訊息長度至五百字以內"
@@ -1745,7 +1798,7 @@ def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript_state
1745
 
1746
  # 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
1747
  if chat_history is not None and len(chat_history) > 10:
1748
- error_msg = "此次對話超過上限"
1749
  raise gr.Error(error_msg)
1750
 
1751
  try:
@@ -1754,14 +1807,14 @@ def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript_state
1754
 
1755
  client = OPEN_AI_CLIENT
1756
  # 直接安排逐字稿資料 in instructions
1757
- if isinstance(trascript_state, str):
1758
- trascript_json = json.loads(trascript_state)
1759
- else:
1760
- trascript_json = trascript_state
1761
- # 移除 embed_url, screenshot_path
1762
- for entry in trascript_json:
1763
- entry.pop('end_time', None)
1764
- trascript_text = json.dumps(trascript_json, ensure_ascii=False)
1765
 
1766
  if isinstance(key_moments, str):
1767
  key_moments_json = json.loads(key_moments)
@@ -1771,7 +1824,7 @@ def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript_state
1771
  for moment in key_moments_json:
1772
  moment.pop('images', None)
1773
  moment.pop('end', None)
1774
- moment.pop('text', None)
1775
  key_moments_text = json.dumps(key_moments_json, ensure_ascii=False)
1776
 
1777
  instructions = get_instructions(content_subject, content_grade, key_moments_text)
@@ -1785,18 +1838,11 @@ def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript_state
1785
  else:
1786
  thread = client.beta.threads.retrieve(thread_id)
1787
 
1788
- user_msg_note = """\n (請一定要用繁體中文回答 zh-TW,
1789
- 請嚴格遵循instructions,擔任一位蘇格拉底家教,
1790
- 並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,
1791
- 不用提到「逐字稿」這個詞,用「內容」代替),
1792
- 回答時請用數學符號代替文字(Latex 用 $ 字號 render)
1793
- """
1794
- user_msg_note = user_msg_note.replace(" ","").replace("\n","")
1795
  # 向线程添加用户的消息
1796
  client.beta.threads.messages.create(
1797
  thread_id=thread.id,
1798
  role="user",
1799
- content=user_message + user_msg_note
1800
  )
1801
 
1802
  # 运行助手,生成响应
@@ -1900,9 +1946,12 @@ def poll_run_status(run_id, thread_id, timeout=600, poll_interval=5):
1900
 
1901
  return run.status
1902
 
1903
- def streaming_chat_with_open_ai(user_message, chat_history, password, thread_id, trascript, key_moments, content_subject, content_grade):
1904
  verify_password(password)
1905
 
 
 
 
1906
  print("===streaming_chat_with_open_ai===")
1907
  print(thread_id)
1908
 
@@ -1912,8 +1961,8 @@ def streaming_chat_with_open_ai(user_message, chat_history, password, thread_id,
1912
  raise gr.Error(error_msg)
1913
 
1914
  # 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
1915
- if chat_history is not None and len(chat_history) > 10:
1916
- error_msg = "此次對話超過上限"
1917
  raise gr.Error(error_msg)
1918
 
1919
  try:
@@ -1921,13 +1970,13 @@ def streaming_chat_with_open_ai(user_message, chat_history, password, thread_id,
1921
  # assistant_id = "asst_5SaUElqvL3U0ybSi9PRM8x3P" #GPT 3.5 turbo
1922
  client = OPEN_AI_CLIENT
1923
  # 直接安排逐字稿資料 in instructions
1924
- if isinstance(trascript, str):
1925
- trascript_json = json.loads(trascript)
1926
- else:
1927
- trascript_json = trascript
1928
- trascript_text = json.dumps(trascript_json, ensure_ascii=False)
1929
- # trascript_text 移除 \n, 空白
1930
- trascript_text = trascript_text.replace("\n", "").replace(" ", "")
1931
 
1932
  if isinstance(key_moments, str):
1933
  key_moments_json = json.loads(key_moments)
@@ -1952,7 +2001,7 @@ def streaming_chat_with_open_ai(user_message, chat_history, password, thread_id,
1952
  client.beta.threads.messages.create(
1953
  thread_id=thread.id,
1954
  role="user",
1955
- content=user_message + "/n (請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,不用提到「逐字稿」這個詞,用「內容」代替)),請在回答的最後標註【參考資料:(時):(分):(秒)】,(如果是反問學生,就只問一個問題,請幫助學生更好的理解資料,字數在100字以內)"
1956
  )
1957
 
1958
  with client.beta.threads.runs.stream(
@@ -1977,6 +2026,21 @@ def create_thread_id():
1977
  print(f"create new thread_id: {thread_id}")
1978
  return thread_id
1979
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1980
  # --- Slide mode ---
1981
  def update_slide(direction):
1982
  global TRANSCRIPTS
@@ -2021,6 +2085,13 @@ def init_params(text, request: gr.Request):
2021
  reading_passage_admin = gr.update(visible=True)
2022
  summary_admin = gr.update(visible=True)
2023
  see_detail = gr.update(visible=True)
 
 
 
 
 
 
 
2024
 
2025
  # if youtube_link in query_params
2026
  if "youtube_id" in request.query_params:
@@ -2036,8 +2107,14 @@ def init_params(text, request: gr.Request):
2036
  reading_passage_admin = gr.update(visible=False)
2037
  summary_admin = gr.update(visible=False)
2038
  see_detail = gr.update(visible=False)
 
 
 
2039
 
2040
- return admin, reading_passage_admin, summary_admin, see_detail, password_text, youtube_link
 
 
 
2041
 
2042
  def update_state(content_subject, content_grade, trascript, key_moments, question_1, question_2, question_3):
2043
  # inputs=[content_subject, content_grade, df_string_output],
@@ -2138,14 +2215,68 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2138
  key_moments_state = gr.State() # 使用 gr.State 存储 key_moments
2139
  streaming_chat_thread_id_state = gr.State() # 使用 gr.State 存储 streaming_chat_thread_id
2140
  with gr.Tab("AI小精靈"):
2141
- with gr.Row():
2142
- with gr.Tab("飛特精靈"):
2143
- bot_avatar = "https://junyi-avatar.s3.ap-northeast-1.amazonaws.com/live/%20%20foxcat-star-18.png?v=20231113095823614"
2144
- user_avatar = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2145
  latex_delimiters = [{"left": "$", "right": "$", "display": False}]
2146
- chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="OPEN AI", show_share_button=False, likeable=True, show_label=False, latex_delimiters=latex_delimiters)
2147
- thread_id = gr.Textbox(label="thread_id", visible=False)
2148
- socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
2149
  with gr.Row():
2150
  with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion:
2151
  btn_1 = gr.Button("問題一")
@@ -2153,12 +2284,19 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2153
  btn_3 = gr.Button("問題一")
2154
  gr.Markdown("### 重新生成問題")
2155
  btn_create_question = gr.Button("生成其他問題", variant="primary")
2156
- openai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath", max_length=60)
2157
  with gr.Row():
2158
  msg = gr.Textbox(label="訊息",scale=3)
2159
  send_button = gr.Button("送出", variant="primary", scale=1)
2160
- with gr.Tab("飛特音速"):
2161
- additional_inputs = [password, streaming_chat_thread_id_state, trascript_state, key_moments_state, content_subject_state, content_grade_state]
 
 
 
 
 
 
 
2162
  streaming_chat = gr.ChatInterface(
2163
  fn=streaming_chat_with_open_ai,
2164
  additional_inputs=additional_inputs,
@@ -2166,41 +2304,41 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2166
  retry_btn=None,
2167
  undo_btn="⏪ 上一步",
2168
  clear_btn="🗑️ 清除全部",
2169
- stop_btn="🛑 停止",
 
2170
  )
2171
- with gr.Tab("其他精靈"):
2172
- ai_name = gr.Dropdown(label="選擇 AI 助理", choices=["jutor", "claude3", "groq"], value="jutor")
2173
- 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)
 
 
 
 
 
 
 
 
 
 
 
2174
  ai_chatbot_socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
2175
  with gr.Row():
2176
  with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion_2:
2177
  ai_chatbot_question_1 = gr.Button("問題一")
2178
  ai_chatbot_question_2 = gr.Button("問題一")
2179
  ai_chatbot_question_3 = gr.Button("問題一")
2180
- ai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath", max_length=60)
2181
  with gr.Row():
2182
  ai_msg = gr.Textbox(label="訊息輸入",scale=3)
2183
  ai_send_button = gr.Button("送出", variant="primary",scale=1)
2184
  with gr.Tab("文章模式"):
2185
- with gr.Row() as reading_passage_admin:
2186
- reading_passage_kind = gr.Textbox(value="reading_passage", show_label=False)
2187
- reading_passage_edit_button = gr.Button("編輯", size="sm", variant="primary")
2188
- reading_passage_update_button = gr.Button("更新", size="sm", variant="primary")
2189
- reading_passage_delete_button = gr.Button("刪除", size="sm", variant="primary")
2190
- reading_passage_create_button = gr.Button("建立", size="sm", variant="primary")
2191
  with gr.Row():
2192
- reading_passage = gr.Textbox(label="Reading Passage", lines=40, show_label=False)
2193
  reading_passage_speak_button = gr.Button("Speak", visible=False)
2194
  reading_passage_audio_output = gr.Audio(label="Audio Output", visible=False)
2195
  with gr.Tab("重點摘要"):
2196
- with gr.Row() as summary_admmin:
2197
- summary_kind = gr.Textbox(value="summary", show_label=False)
2198
- summary_edit_button = gr.Button("編輯", size="sm", variant="primary")
2199
- summary_update_button = gr.Button("更新", size="sm", variant="primary")
2200
- summary_delete_button = gr.Button("刪除", size="sm", variant="primary")
2201
- summary_create_button = gr.Button("建立", size="sm", variant="primary")
2202
  with gr.Row():
2203
- df_summarise = gr.Textbox(container=True, show_copy_button=True, lines=40, show_label=False)
2204
  with gr.Tab("關鍵時刻"):
2205
  with gr.Row():
2206
  key_moments_html = gr.HTML(value="")
@@ -2221,16 +2359,16 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2221
  worksheet_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2222
  worksheet_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
2223
  worksheet_exam_result_retrun_original = gr.Button("返回原始結果")
2224
- with gr.Accordion("prompt", open=False):
2225
  worksheet_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
2226
  with gr.Column(scale=2):
2227
  # 生成對應不同模式的結果
2228
  worksheet_exam_result_prompt = gr.Textbox(visible=False)
2229
  worksheet_exam_result_original = gr.Textbox(visible=False)
2230
- worksheet_exam_result = gr.Textbox(label="初次生成結果", show_copy_button=True, interactive=True, lines=40)
 
2231
  worksheet_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
2232
  worksheet_exam_result_word_link = gr.File(label="Download Word")
2233
-
2234
  with gr.Tab("課程計畫"):
2235
  with gr.Row():
2236
  with gr.Column(scale=1):
@@ -2242,17 +2380,16 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2242
  lesson_plan_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2243
  lesson_plan_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
2244
  lesson_plan_exam_result_retrun_original = gr.Button("返回原始結果")
2245
- with gr.Accordion("prompt", open=False):
2246
  lesson_plan_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
2247
  with gr.Column(scale=2):
2248
  # 生成對應不同模式的結果
2249
  lesson_plan_exam_result_prompt = gr.Textbox(visible=False)
2250
  lesson_plan_exam_result_original = gr.Textbox(visible=False)
2251
- lesson_plan_exam_result = gr.Textbox(label="初次生成結果", show_copy_button=True, interactive=True, lines=40)
2252
 
2253
  lesson_plan_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
2254
  lesson_plan_exam_result_word_link = gr.File(label="Download Word")
2255
-
2256
  with gr.Tab("出場券"):
2257
  with gr.Row():
2258
  with gr.Column(scale=1):
@@ -2264,13 +2401,13 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2264
  exit_ticket_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2265
  exit_ticket_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
2266
  exit_ticket_exam_result_retrun_original = gr.Button("返回原始結果")
2267
- with gr.Accordion("prompt", open=False):
2268
  exit_ticket_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
2269
  with gr.Column(scale=2):
2270
  # 生成對應不同模式的結果
2271
  exit_ticket_exam_result_prompt = gr.Textbox(visible=False)
2272
  exit_ticket_exam_result_original = gr.Textbox(visible=False)
2273
- exit_ticket_exam_result = gr.Textbox(label="初次生成結果", show_copy_button=True, interactive=True, lines=40)
2274
 
2275
  exit_ticket_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
2276
  exit_ticket_exam_result_word_link = gr.File(label="Download Word")
@@ -2294,17 +2431,39 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2294
  with gr.Tab("逐字稿本文"):
2295
  with gr.Row() as transcript_admmin:
2296
  transcript_kind = gr.Textbox(value="transcript", show_label=False)
 
2297
  transcript_edit_button = gr.Button("編輯", size="sm", variant="primary")
2298
- transcript_update_button = gr.Button("更新", size="sm", variant="primary")
2299
  transcript_delete_button = gr.Button("刪除", size="sm", variant="primary")
2300
  transcript_create_button = gr.Button("建立", size="sm", variant="primary")
2301
  with gr.Row():
2302
  df_string_output = gr.Textbox(lines=40, label="Data Text", interactive=False, show_copy_button=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2303
  with gr.Tab("關鍵時刻本文"):
2304
  with gr.Row() as key_moments_admin:
2305
  key_moments_kind = gr.Textbox(value="key_moments", show_label=False)
 
2306
  key_moments_edit_button = gr.Button("編輯", size="sm", variant="primary")
2307
- key_moments_update_button = gr.Button("更新", size="sm", variant="primary")
2308
  key_moments_delete_button = gr.Button("刪除", size="sm", variant="primary")
2309
  key_moments_create_button = gr.Button("建立", size="sm", variant="primary")
2310
  with gr.Row():
@@ -2312,8 +2471,9 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2312
  with gr.Tab("問題本文"):
2313
  with gr.Row() as question_list_admin:
2314
  questions_kind = gr.Textbox(value="questions", show_label=False)
 
2315
  questions_edit_button = gr.Button("編輯", size="sm", variant="primary")
2316
- questions_update_button = gr.Button("更新", size="sm", variant="primary")
2317
  questions_delete_button = gr.Button("刪除", size="sm", variant="primary")
2318
  questions_create_button = gr.Button("建立", size="sm", variant="primary")
2319
  with gr.Row():
@@ -2337,11 +2497,29 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2337
  mind_map_html = gr.HTML()
2338
 
2339
  # --- Event ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2340
  # OPENAI ASSISTANT CHATBOT 模式
2341
  send_button.click(
2342
  chat_with_opan_ai_assistant,
2343
- inputs=[password, video_id, thread_id, trascript_state, key_moments, msg, chatbot, content_subject, content_grade, socratic_mode_btn],
2344
- outputs=[msg, chatbot, thread_id]
 
2345
  )
2346
  openai_chatbot_audio_input.change(
2347
  process_open_ai_audio_to_chatbot,
@@ -2349,23 +2527,26 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2349
  outputs=[msg]
2350
  )
2351
  # OPENAI ASSISTANT CHATBOT 連接按鈕點擊事件
2352
- btn_1_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, trascript_state, key_moments, btn_1, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
2353
- btn_2_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, trascript_state, key_moments, btn_2, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
2354
- btn_3_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, trascript_state, key_moments, btn_3, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
2355
  btn_1.click(
2356
  chat_with_opan_ai_assistant,
2357
  inputs=btn_1_chat_with_opan_ai_assistant_input,
2358
- outputs=[msg, chatbot, thread_id]
 
2359
  )
2360
  btn_2.click(
2361
  chat_with_opan_ai_assistant,
2362
  inputs=btn_2_chat_with_opan_ai_assistant_input,
2363
- outputs=[msg, chatbot, thread_id]
 
2364
  )
2365
  btn_3.click(
2366
  chat_with_opan_ai_assistant,
2367
  inputs=btn_3_chat_with_opan_ai_assistant_input,
2368
- outputs=[msg, chatbot, thread_id]
 
2369
  )
2370
  btn_create_question.click(
2371
  change_questions,
@@ -2373,30 +2554,34 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2373
  outputs = [btn_1, btn_2, btn_3]
2374
  )
2375
 
2376
- # ai_chatbot 模式
2377
  ai_send_button.click(
2378
  chat_with_ai,
2379
- inputs=[ai_name, password, video_id, trascript_state, key_moments, ai_msg, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn],
2380
- outputs=[ai_msg, ai_chatbot]
 
2381
  )
2382
- # ai_chatbot 连接按钮点击事件
2383
- ai_chatbot_question_1_chat_with_ai_input =[ai_name, password, video_id, trascript_state, key_moments, ai_chatbot_question_1, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
2384
- ai_chatbot_question_2_chat_with_ai_input =[ai_name, password, video_id, trascript_state, key_moments, ai_chatbot_question_2, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
2385
- ai_chatbot_question_3_chat_with_ai_input =[ai_name, password, video_id, trascript_state, key_moments, ai_chatbot_question_3, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
2386
  ai_chatbot_question_1.click(
2387
  chat_with_ai,
2388
  inputs=ai_chatbot_question_1_chat_with_ai_input,
2389
- outputs=[ai_msg, ai_chatbot]
 
2390
  )
2391
  ai_chatbot_question_2.click(
2392
  chat_with_ai,
2393
  inputs=ai_chatbot_question_2_chat_with_ai_input,
2394
- outputs=[ai_msg, ai_chatbot]
 
2395
  )
2396
  ai_chatbot_question_3.click(
2397
  chat_with_ai,
2398
  inputs=ai_chatbot_question_3_chat_with_ai_input,
2399
- outputs=[ai_msg, ai_chatbot]
 
2400
  )
2401
 
2402
  # file_upload.change(process_file, inputs=file_upload, outputs=df_string_output)
@@ -2411,6 +2596,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2411
  btn_2,
2412
  btn_3,
2413
  df_string_output,
 
2414
  df_summarise,
2415
  key_moments,
2416
  key_moments_html,
@@ -2420,6 +2606,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2420
  simple_html_content,
2421
  slide_image,
2422
  slide_text,
 
2423
  reading_passage,
2424
  content_subject,
2425
  content_grade,
@@ -2469,50 +2656,65 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2469
  # web_link.change(process_web_link, inputs=web_link, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
2470
 
2471
  # reading_passage event
 
 
 
 
 
2472
  reading_passage_create_button.click(
2473
  create_LLM_content,
2474
  inputs=[video_id, df_string_output, reading_passage_kind],
2475
- outputs=[reading_passage]
2476
  )
2477
  reading_passage_delete_button.click(
2478
  delete_LLM_content,
2479
  inputs=[video_id, reading_passage_kind],
2480
- outputs=[reading_passage]
2481
  )
2482
  reading_passage_edit_button.click(
2483
  enable_edit_mode,
2484
  inputs=[],
2485
- outputs=[reading_passage]
2486
  )
2487
  reading_passage_update_button.click(
2488
  update_LLM_content,
2489
- inputs=[video_id, reading_passage, reading_passage_kind],
2490
- outputs=[reading_passage]
2491
  )
2492
 
2493
  # summary event
 
 
 
 
 
2494
  summary_create_button.click(
2495
  create_LLM_content,
2496
  inputs=[video_id, df_string_output, summary_kind],
2497
- outputs=[df_summarise]
2498
  )
2499
  summary_delete_button.click(
2500
  delete_LLM_content,
2501
  inputs=[video_id, summary_kind],
2502
- outputs=[df_summarise]
2503
  )
2504
  summary_edit_button.click(
2505
  enable_edit_mode,
2506
  inputs=[],
2507
- outputs=[df_summarise]
2508
  )
2509
  summary_update_button.click(
2510
  update_LLM_content,
2511
- inputs=[video_id, df_summarise, summary_kind],
2512
- outputs=[df_summarise]
2513
  )
2514
 
2515
  # transcript event
 
 
 
 
 
2516
  transcript_create_button.click(
2517
  create_LLM_content,
2518
  inputs=[video_id, df_string_output, transcript_kind],
@@ -2535,6 +2737,11 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2535
  )
2536
 
2537
  # key_moments event
 
 
 
 
 
2538
  key_moments_create_button.click(
2539
  create_LLM_content,
2540
  inputs=[video_id, df_string_output, key_moments_kind],
@@ -2557,6 +2764,11 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2557
  )
2558
 
2559
  # question_list event
 
 
 
 
 
2560
  questions_create_button.click(
2561
  create_LLM_content,
2562
  inputs=[video_id, df_string_output, questions_kind],
@@ -2643,10 +2855,24 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2643
  )
2644
 
2645
  # init_params
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2646
  demo.load(
2647
  init_params,
2648
  inputs =[youtube_link],
2649
- outputs = [admin, reading_passage_admin, summary_admmin, see_details, password , youtube_link]
2650
  )
2651
 
2652
  demo.launch(allowed_paths=["videos"])
 
367
  else:
368
  return None
369
 
370
+ def get_transcript_by_yt_api(video_id):
371
  languages = ['zh-TW', 'zh-Hant', 'zh', 'en'] # 優先順序列表
372
  for language in languages:
373
  try:
 
380
  continue # 當前語言的字幕沒有找到,繼續嘗試下一個語言
381
  return None # 所有嘗試都失敗,返回None
382
 
383
+ def generate_transcription_by_whisper(video_id):
384
  youtube_url = f'https://www.youtube.com/watch?v={video_id}'
385
  codec_name = "mp3"
386
  outtmpl = f"{OUTPUT_PATH}/{video_id}.%(ext)s"
 
457
  print("逐字稿文件不存在于GCS中,重新建立")
458
  # 从YouTube获取逐字稿并上传
459
  try:
460
+ transcript = get_transcript_by_yt_api(video_id)
461
  except:
462
  # call open ai whisper
463
  print("===call open ai whisper===")
464
+ transcript = generate_transcription_by_whisper(video_id)
465
 
466
  if transcript:
467
  print("成功獲取字幕")
468
  else:
469
  print("沒有找到字幕")
470
+ transcript = generate_transcription_by_whisper(video_id)
471
 
472
  transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
473
  upload_file_to_gcs_with_json_string(gcs_client, bucket_name, transcript_blob_name, transcript_text)
 
572
  questions = get_questions(video_id, formatted_simple_transcript, source)
573
  questions_json = json.dumps(questions, ensure_ascii=False, indent=2)
574
  summary_json = get_video_id_summary(video_id, formatted_simple_transcript, source)
575
+ summary_text = summary_json["summary"]
576
  summary = summary_json["summary"]
577
  key_moments_json = get_key_moments(video_id, formatted_simple_transcript, formatted_transcript, source)
578
  key_moments = key_moments_json["key_moments"]
 
587
  mind_map = mind_map_json["mind_map"]
588
  mind_map_html = get_mind_map_html(mind_map)
589
  reading_passage_json = get_reading_passage(video_id, formatted_simple_transcript, source)
590
+ reading_passage_text = reading_passage_json["reading_passage"]
591
  reading_passage = reading_passage_json["reading_passage"]
592
  meta_data = get_meta_data(video_id)
593
  subject = meta_data["subject"]
 
600
  questions[1] if len(questions) > 1 else "", \
601
  questions[2] if len(questions) > 2 else "", \
602
  original_transcript, \
603
+ summary_text, \
604
  summary, \
605
  key_moments_text, \
606
  key_moments_html, \
 
610
  simple_html_content, \
611
  first_image, \
612
  first_text, \
613
+ reading_passage_text, \
614
  reading_passage, \
615
  subject, \
616
  grade
 
754
  請一定要使用繁體中文 zh-TW,並用台灣人的口語
755
  產生的結果不要前後文解釋,也不要敘述這篇文章怎麼產生的
756
  只需要專注提供 Reading Passage,字數在 500 字以內
757
+ 敘述中,請把數學或是專業術語,用 Latex 包覆($...$),並且不要去改原本的文章
758
+ 加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
759
  """
760
  messages = [
761
  {"role": "system", "content": sys_content},
 
763
  ]
764
 
765
  request_payload = {
766
+ "model": "gpt-4-turbo",
767
  "messages": messages,
768
  "max_tokens": 4000,
769
  }
 
840
  ]
841
 
842
  request_payload = {
843
+ "model": "gpt-4-turbo",
844
  "messages": messages,
845
  "max_tokens": 4000,
846
  }
 
922
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
923
  user_content = f"""
924
  請根據 {df_string},判斷這份文本
925
+ 格式為 Markdown
 
 
926
  整體摘要在一百字以內
927
+ 重點概念列出 bullet points,至少三個,最多五個
 
 
928
  以及可能的結論與結尾延伸小問題提供學生作反思
929
+ 敘述中,請把數學或是專業術語,用 Latex 包覆($...$)
930
+ 加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
931
 
932
  整體格式為:
933
+ ## 📚 整體摘要
934
+ - (一個 bullet point....)
935
+
936
+ ## 🔖 重點概念
937
+ - xxx
938
+ - xxx
939
+ - xxx
940
+
941
+ ## 💡 為什麼我們要學這個?
942
+ - (一個 bullet point....)
943
+
944
+ ## ❓ 延伸小問題
945
+ - (一個 bullet point....)
946
  """
947
 
948
  # 🗂️ 1. 內容類型:?
 
958
  ]
959
 
960
  request_payload = {
961
+ "model": "gpt-4-turbo",
962
  "messages": messages,
963
  "max_tokens": 4000,
964
  }
 
1025
 
1026
  def generate_questions(df_string):
1027
  # 使用 OpenAI 生成基于上传数据的问题
1028
+ if isinstance(df_string, str):
1029
+ df_string_json = json.loads(df_string)
1030
+ else:
1031
+ df_string_json = df_string
1032
+ content_text = ""
1033
+
1034
+ for entry in df_string_json:
1035
+ content_text += entry["text"] + ","
1036
 
1037
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,並用既有資料為本質猜測用戶可能會問的問題,使用 zh-TW"
1038
+ user_content = f"請根據 {content_text} 生成三個問題,並用 JSON 格式返回 questions:[q1的敘述text, q2的敘述text, q3的敘述text]"
1039
  messages = [
1040
  {"role": "system", "content": sys_content},
1041
  {"role": "user", "content": user_content}
 
1048
 
1049
 
1050
  request_payload = {
1051
+ "model": "gpt-4-turbo",
1052
  "messages": messages,
1053
  "max_tokens": 4000,
1054
  "response_format": response_format
 
1166
  response_format = { "type": "json_object" }
1167
 
1168
  request_payload = {
1169
+ "model": "gpt-4-turbo",
1170
  "messages": messages,
1171
  "max_tokens": 4096,
1172
  "response_format": response_format
 
1208
  {"role": "user", "content": user_content}
1209
  ]
1210
  request_payload = {
1211
+ "model": "gpt-4-turbo",
1212
  "messages": messages,
1213
  "max_tokens": 100,
1214
  }
 
1421
  return key_moments_html
1422
 
1423
  # ---- LLM CRUD ----
1424
+ def get_LLM_content(video_id, kind):
1425
+ print(f"===get_{kind}===")
1426
+ gcs_client = GCS_CLIENT
1427
+ bucket_name = 'video_ai_assistant'
1428
+ file_name = f'{video_id}_{kind}.json'
1429
+ blob_name = f"{video_id}/{file_name}"
1430
+ # 检查 file 是否存在
1431
+ is_file_exists = GCS_SERVICE.check_file_exists(bucket_name, blob_name)
1432
+ if is_file_exists:
1433
+ content = download_blob_to_string(gcs_client, bucket_name, blob_name)
1434
+ content_json = json.loads(content)
1435
+ if kind == "reading_passage":
1436
+ content_text = content_json["reading_passage"]
1437
+ elif kind == "summary":
1438
+ content_text = content_json["summary"]
1439
+ else:
1440
+ content_text = json.dumps(content_json, ensure_ascii=False, indent=2)
1441
+ else:
1442
+ content_text = ""
1443
+ return content_text
1444
+
1445
  def enable_edit_mode():
1446
  return gr.update(interactive=True)
1447
 
 
1466
  blob_name = f"{video_id}/{file_name}"
1467
 
1468
  if kind == "reading_passage":
1469
+ print("=========reading_passage=======")
1470
+ print(new_content)
1471
  reading_passage_json = {"reading_passage": str(new_content)}
1472
  reading_passage_text = json.dumps(reading_passage_json, ensure_ascii=False, indent=2)
1473
  upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, reading_passage_text)
1474
+ updated_content = new_content
1475
  elif kind == "summary":
1476
  summary_json = {"summary": str(new_content)}
1477
  summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
1478
  upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, summary_text)
1479
+ updated_content = new_content
1480
  elif kind == "mind_map":
1481
  mind_map_json = {"mind_map": str(new_content)}
1482
  mind_map_text = json.dumps(mind_map_json, ensure_ascii=False, indent=2)
 
1549
 
1550
  return gr.update(value=content, interactive=False)
1551
 
 
1552
  # AI 生成教學素材
1553
  def get_meta_data(video_id, source="gcs"):
1554
  if source == "gcs":
 
1652
  prompt = material.generate_content_prompt()
1653
  user_content = material.build_user_content()
1654
  messages = material.build_messages(user_content)
1655
+ ai_model_name = "gpt-4-turbo"
1656
  request_payload = {
1657
  "model": ai_model_name,
1658
  "messages": messages,
 
1666
  material = EducationalMaterial(df_string_output, "", "", "", "", "")
1667
  user_content = material.build_fine_tune_user_content(exam_result_prompt, exam_result, exam_result_fine_tune_prompt)
1668
  messages = material.build_messages(user_content)
1669
+ ai_model_name = "gpt-4-turbo"
1670
  request_payload = {
1671
  "model": ai_model_name,
1672
  "messages": messages,
 
1702
  Language: Traditional Chinese ZH-TW (it's very important), suitable for {content_grade} th-grade level.
1703
  Response:
1704
  - Single question, under 100 characters
1705
+ - include math symbols (use LaTeX $ to cover before and after, ex: $x^2$)
1706
  - hint with video timestamp which format 【參考:00:00:00】.
1707
  - Sometimes encourage user by Taiwanese style with relaxing atmosphere.
1708
  - if user ask questions not include in context,
 
1711
  """
1712
  return instructions
1713
 
1714
+ 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):
1715
  verify_password(password)
1716
 
1717
+ print("=====user_data=====")
1718
+ print(f"user_data: {user_data}")
1719
+
1720
+ if chat_history is not None and len(chat_history) > 11:
1721
+ error_msg = "此次對話超過上限(對話一輪10次)"
1722
  raise gr.Error(error_msg)
1723
 
1724
+ if not ai_name in ["jutor", "claude3", "groq"]:
1725
+ ai_name = "jutor"
1726
+
1727
  if ai_name == "jutor":
1728
  ai_client = ""
1729
  elif ai_name == "claude3":
1730
  ai_client = BEDROCK_CLIENT
1731
  elif ai_name == "groq":
1732
  ai_client = GROQ_CLIENT
1733
+ else:
1734
+ ai_client = ""
1735
+
1736
  if isinstance(trascript_state, str):
1737
  simple_transcript = json.loads(trascript_state)
1738
  else:
 
1746
  for moment in key_moments_json:
1747
  moment.pop('images', None)
1748
  moment.pop('end', None)
1749
+ moment.pop('transcript', None)
1750
  key_moments_text = json.dumps(key_moments_json, ensure_ascii=False)
1751
 
1752
  instructions = get_instructions(content_subject, content_grade, key_moments_text)
 
1785
  print(f"Error: {e}")
1786
  return "请求失败,请稍后再试!", chat_history
1787
 
1788
+ 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):
1789
  verify_password(password)
1790
 
1791
+ print("=====user_data=====")
1792
+ print(f"user_data: {user_data}")
1793
+
1794
  # 先計算 user_message 是否超過 500 個字
1795
  if len(user_message) > 1500:
1796
  error_msg = "你的訊息太長了,請縮短訊息長度至五百字以內"
 
1798
 
1799
  # 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
1800
  if chat_history is not None and len(chat_history) > 10:
1801
+ error_msg = "此次對話超過上限(對話一輪10次)"
1802
  raise gr.Error(error_msg)
1803
 
1804
  try:
 
1807
 
1808
  client = OPEN_AI_CLIENT
1809
  # 直接安排逐字稿資料 in instructions
1810
+ # if isinstance(trascript_state, str):
1811
+ # trascript_json = json.loads(trascript_state)
1812
+ # else:
1813
+ # trascript_json = trascript_state
1814
+ # # 移除 embed_url, screenshot_path
1815
+ # for entry in trascript_json:
1816
+ # entry.pop('end_time', None)
1817
+ # trascript_text = json.dumps(trascript_json, ensure_ascii=False)
1818
 
1819
  if isinstance(key_moments, str):
1820
  key_moments_json = json.loads(key_moments)
 
1824
  for moment in key_moments_json:
1825
  moment.pop('images', None)
1826
  moment.pop('end', None)
1827
+ moment.pop('transcript', None)
1828
  key_moments_text = json.dumps(key_moments_json, ensure_ascii=False)
1829
 
1830
  instructions = get_instructions(content_subject, content_grade, key_moments_text)
 
1838
  else:
1839
  thread = client.beta.threads.retrieve(thread_id)
1840
 
 
 
 
 
 
 
 
1841
  # 向线程添加用户的消息
1842
  client.beta.threads.messages.create(
1843
  thread_id=thread.id,
1844
  role="user",
1845
+ content=user_message + "/n 請嚴格遵循instructions,擔任一位蘇格拉底家教,絕對不要重複 user 的問句,請用引導的方式指引方向,請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,請在回答的最後標註【參考:(時):(分):(秒)】,(如果是反問學生,就只問一個問題,請幫助學生更好的理解資料,字數在100字以內,回答時請用數學符號代替文字(Latex 用 $ 字號 render, ex: $x^2$)"
1846
  )
1847
 
1848
  # 运行助手,生成响应
 
1946
 
1947
  return run.status
1948
 
1949
+ def streaming_chat_with_open_ai(user_message, chat_history, password, user_data, thread_id, trascript, key_moments, content_subject, content_grade):
1950
  verify_password(password)
1951
 
1952
+ print("=====user_data=====")
1953
+ print(f"user_data: {user_data}")
1954
+
1955
  print("===streaming_chat_with_open_ai===")
1956
  print(thread_id)
1957
 
 
1961
  raise gr.Error(error_msg)
1962
 
1963
  # 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
1964
+ if chat_history is not None and len(chat_history) > 11:
1965
+ error_msg = "此次對話超過上限(對話一輪10次)"
1966
  raise gr.Error(error_msg)
1967
 
1968
  try:
 
1970
  # assistant_id = "asst_5SaUElqvL3U0ybSi9PRM8x3P" #GPT 3.5 turbo
1971
  client = OPEN_AI_CLIENT
1972
  # 直接安排逐字稿資料 in instructions
1973
+ # if isinstance(trascript, str):
1974
+ # trascript_json = json.loads(trascript)
1975
+ # else:
1976
+ # trascript_json = trascript
1977
+ # trascript_text = json.dumps(trascript_json, ensure_ascii=False)
1978
+ # # trascript_text 移除 \n, 空白
1979
+ # trascript_text = trascript_text.replace("\n", "").replace(" ", "")
1980
 
1981
  if isinstance(key_moments, str):
1982
  key_moments_json = json.loads(key_moments)
 
2001
  client.beta.threads.messages.create(
2002
  thread_id=thread.id,
2003
  role="user",
2004
+ content=user_message + "/n 請嚴格遵循instructions,擔任一位蘇格拉底家教,請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,不用提到「逐字稿」這個詞,用「內容」代替)),請在回答的最後標註【參考資料:(時):(分):(秒)】,(如果是反問學生,就只問一個問題,請幫助學生更好的理解資料,字數在100字以內)"
2005
  )
2006
 
2007
  with client.beta.threads.runs.stream(
 
2026
  print(f"create new thread_id: {thread_id}")
2027
  return thread_id
2028
 
2029
+ def chatbot_select(chatbot_name):
2030
+ chatbot_select_accordion_visible = gr.update(open=False)
2031
+ chatbot_open_ai_visible = gr.update(visible=False)
2032
+ chatbot_open_ai_streaming_visible = gr.update(visible=False)
2033
+ chatbot_jutor_visible = gr.update(visible=False)
2034
+
2035
+ if chatbot_name == "chatbot_open_ai":
2036
+ chatbot_open_ai_visible = gr.update(visible=True)
2037
+ elif chatbot_name == "chatbot_open_ai_streaming":
2038
+ chatbot_open_ai_streaming_visible = gr.update(visible=True)
2039
+ elif chatbot_name == "chatbot_jutor":
2040
+ chatbot_jutor_visible = gr.update(visible=True)
2041
+
2042
+ return chatbot_select_accordion_visible, chatbot_open_ai_visible, chatbot_open_ai_streaming_visible, chatbot_jutor_visible
2043
+
2044
  # --- Slide mode ---
2045
  def update_slide(direction):
2046
  global TRANSCRIPTS
 
2085
  reading_passage_admin = gr.update(visible=True)
2086
  summary_admin = gr.update(visible=True)
2087
  see_detail = gr.update(visible=True)
2088
+ worksheet_accordion = gr.update(visible=True)
2089
+ lesson_plan_accordion = gr.update(visible=True)
2090
+ exit_ticket_accordion = gr.update(visible=True)
2091
+
2092
+ chatbot_open_ai = gr.update(visible=False)
2093
+ chatbot_open_ai_streaming = gr.update(visible=False)
2094
+ chatbot_jutor = gr.update(visible=False)
2095
 
2096
  # if youtube_link in query_params
2097
  if "youtube_id" in request.query_params:
 
2107
  reading_passage_admin = gr.update(visible=False)
2108
  summary_admin = gr.update(visible=False)
2109
  see_detail = gr.update(visible=False)
2110
+ worksheet_accordion = gr.update(visible=False)
2111
+ lesson_plan_accordion = gr.update(visible=False)
2112
+ exit_ticket_accordion = gr.update(visible=False)
2113
 
2114
+ return admin, reading_passage_admin, summary_admin, see_detail, \
2115
+ worksheet_accordion, lesson_plan_accordion, exit_ticket_accordion, \
2116
+ password_text, youtube_link, \
2117
+ chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor
2118
 
2119
  def update_state(content_subject, content_grade, trascript, key_moments, question_1, question_2, question_3):
2120
  # inputs=[content_subject, content_grade, df_string_output],
 
2215
  key_moments_state = gr.State() # 使用 gr.State 存储 key_moments
2216
  streaming_chat_thread_id_state = gr.State() # 使用 gr.State 存储 streaming_chat_thread_id
2217
  with gr.Tab("AI小精靈"):
2218
+ with gr.Accordion("選擇 AI 小精靈", open=True) as chatbot_select_accordion:
2219
+ with gr.Row():
2220
+ with gr.Column(scale=1, variant="panel"):
2221
+ chatbot_avatar_url = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
2222
+ chatbot_description = """Hi,我是你的AI學伴【飛特精靈】,\n
2223
+ 我可以陪你一起學習本次的內容,有什麼問題都可以問我喔!\n
2224
+ 🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!\n
2225
+ 🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!\n
2226
+ 🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!\n
2227
+ 💤 但我還在成長,體力有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!\n
2228
+ 🦄 如果達到上限,或是遇到精靈很累,請問問其他朋友,像是飛特音速說話的速度比較快,你是否跟得上呢?你也可以和其他精靈互動看看喔!\n
2229
+ """
2230
+ chatbot_open_ai_name = gr.State("chatbot_open_ai")
2231
+ gr.Image(value=chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
2232
+ chatbot_open_ai_select_btn = gr.Button("👆選擇【飛特精靈】", elem_id="chatbot_btn", visible=True, variant="primary")
2233
+ gr.Markdown(value=chatbot_description, visible=True)
2234
+ with gr.Column(scale=1, variant="panel"):
2235
+ 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"
2236
+ streaming_chatbot_description = """Hi,我是【飛特音速】, \n
2237
+ 說話比較快,但有什麼問題都可以問我喔! \n
2238
+ 🚀 我沒有預設問題、也沒有語音輸入,適合快問快答,一起練習問出好問題吧 \n
2239
+ 🔠 擅長用文字表達的你,可以用鍵盤輸入你的問題,我會盡力回答你的問題喔\n
2240
+ 💤 我還在成長,體力有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔~
2241
+ """
2242
+ chatbot_open_ai_streaming_name = gr.State("chatbot_open_ai_streaming")
2243
+ gr.Image(value=streaming_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
2244
+ chatbot_open_ai_streaming_select_btn = gr.Button("👆選擇【飛特音速】", elem_id="streaming_chatbot_btn", visible=True, variant="primary")
2245
+ gr.Markdown(value=streaming_chatbot_description, visible=True)
2246
+ with gr.Column(scale=1, variant="panel"):
2247
+ jutor_chatbot_avatar_url = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2019/11/%E5%9B%9B%E6%A0%BC%E6%95%85%E4%BA%8B-04.jpg"
2248
+ jutor_chatbot_description = """Hi,我們是【梨梨、麥麥、狐狸貓】,\n
2249
+ 也可以陪你一起學習本次的內容,有什麼問題都可以問我喔!\n
2250
+ 🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!\n
2251
+ 🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!\n
2252
+ 🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!\n
2253
+ 💤 精靈們體力都有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!\n
2254
+ """
2255
+ chatbot_jutor_name = gr.State("chatbot_jutor")
2256
+ gr.Image(value=jutor_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
2257
+ chatbot_jutor_select_btn = gr.Button("👆選擇【梨梨、麥麥、狐狸貓】", elem_id="jutor_chatbot_btn", visible=True, variant="primary")
2258
+ gr.Markdown(value=jutor_chatbot_description, visible=True)
2259
+
2260
+ with gr.Row("飛特精靈") as chatbot_open_ai:
2261
+ with gr.Column():
2262
+ user_avatar = "https://em-content.zobj.net/source/google/263/flushed-face_1f633.png"
2263
+ bot_avatar = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
2264
  latex_delimiters = [{"left": "$", "right": "$", "display": False}]
2265
+ chatbot_greeting = [[
2266
+ None,
2267
+ """Hi,我是你的AI學伴【飛特精靈】,我可以陪你一起學習本次的內容,有什麼問題都可以問我喔!
2268
+ 🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!
2269
+ 🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!
2270
+ 🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!
2271
+ 💤 但我還在成長,體力有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!
2272
+ 🦄 如果達到上限,或是��到精靈很累,請問問其他朋友,像是飛特音速說話的速度比較快,你是否跟得上呢?你也可以和其他精靈互動看看喔!
2273
+ """,
2274
+ ]]
2275
+ with gr.Row():
2276
+ chatbot = gr.Chatbot(avatar_images=[user_avatar, bot_avatar], label="OPEN AI", show_share_button=False, likeable=True, show_label=False, latex_delimiters=latex_delimiters,value=chatbot_greeting)
2277
+ with gr.Row():
2278
+ thread_id = gr.Textbox(label="thread_id", visible=False)
2279
+ socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
2280
  with gr.Row():
2281
  with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion:
2282
  btn_1 = gr.Button("問題一")
 
2284
  btn_3 = gr.Button("問題一")
2285
  gr.Markdown("### 重新生成問題")
2286
  btn_create_question = gr.Button("生成其他問題", variant="primary")
2287
+ openai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath", max_length=60, label="語音輸入")
2288
  with gr.Row():
2289
  msg = gr.Textbox(label="訊息",scale=3)
2290
  send_button = gr.Button("送出", variant="primary", scale=1)
2291
+ with gr.Row("飛特音速") as chatbot_open_ai_streaming:
2292
+ with gr.Column():
2293
+ streaming_chat_greeting = """
2294
+ Hi,我是【飛特音速】,說話比較快,但有什麼問題都可以問我喔! \n
2295
+ 🚀 我沒有預設問題、也沒有語音輸入,適合快問快答的你 \n
2296
+ 🔠 鍵盤輸入你的問題,我會盡力回答你的問題喔!\n
2297
+ 💤 我還在成長,體力有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!
2298
+ """
2299
+ additional_inputs = [password, user_data, streaming_chat_thread_id_state, trascript_state, key_moments_state, content_subject_state, content_grade_state]
2300
  streaming_chat = gr.ChatInterface(
2301
  fn=streaming_chat_with_open_ai,
2302
  additional_inputs=additional_inputs,
 
2304
  retry_btn=None,
2305
  undo_btn="⏪ 上一步",
2306
  clear_btn="🗑️ 清除全部",
2307
+ stop_btn=None,
2308
+ description=streaming_chat_greeting
2309
  )
2310
+ with gr.Row("其他精靈") as chatbot_jutor:
2311
+ with gr.Column():
2312
+ ai_chatbot_greeting = [[
2313
+ None,
2314
+ """Hi,我是飛特精靈的朋友們【梨梨、麥麥、狐狸貓】,也可以陪你一起學習本次的內容,有什麼問題都可以問我喔!
2315
+ 🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!
2316
+ 🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!
2317
+ 🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!
2318
+ 💤 精靈們體力都有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!
2319
+ """,
2320
+ ]]
2321
+ 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"
2322
+ ai_name = gr.Dropdown(label="選擇 AI 助理", choices=[("梨梨","jutor"), ("麥麥","claude3"), ("狐狸貓","groq")], value="jutor")
2323
+ 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)
2324
  ai_chatbot_socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
2325
  with gr.Row():
2326
  with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion_2:
2327
  ai_chatbot_question_1 = gr.Button("問題一")
2328
  ai_chatbot_question_2 = gr.Button("問題一")
2329
  ai_chatbot_question_3 = gr.Button("問題一")
2330
+ ai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath", max_length=60, label="語音輸入")
2331
  with gr.Row():
2332
  ai_msg = gr.Textbox(label="訊息輸入",scale=3)
2333
  ai_send_button = gr.Button("送出", variant="primary",scale=1)
2334
  with gr.Tab("文章模式"):
 
 
 
 
 
 
2335
  with gr.Row():
2336
+ reading_passage = gr.Markdown(show_label=False, latex_delimiters = [{"left": "$", "right": "$", "display": False}])
2337
  reading_passage_speak_button = gr.Button("Speak", visible=False)
2338
  reading_passage_audio_output = gr.Audio(label="Audio Output", visible=False)
2339
  with gr.Tab("重點摘要"):
 
 
 
 
 
 
2340
  with gr.Row():
2341
+ df_summarise = gr.Markdown(show_label=False, latex_delimiters = [{"left": "$", "right": "$", "display": False}])
2342
  with gr.Tab("關鍵時刻"):
2343
  with gr.Row():
2344
  key_moments_html = gr.HTML(value="")
 
2359
  worksheet_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2360
  worksheet_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
2361
  worksheet_exam_result_retrun_original = gr.Button("返回原始結果")
2362
+ with gr.Accordion("prompt", open=False) as worksheet_accordion:
2363
  worksheet_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
2364
  with gr.Column(scale=2):
2365
  # 生成對應不同模式的結果
2366
  worksheet_exam_result_prompt = gr.Textbox(visible=False)
2367
  worksheet_exam_result_original = gr.Textbox(visible=False)
2368
+ # worksheet_exam_result = gr.Textbox(label="初次生成結果", show_copy_button=True, interactive=True, lines=40)
2369
+ worksheet_exam_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
2370
  worksheet_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
2371
  worksheet_exam_result_word_link = gr.File(label="Download Word")
 
2372
  with gr.Tab("課程計畫"):
2373
  with gr.Row():
2374
  with gr.Column(scale=1):
 
2380
  lesson_plan_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2381
  lesson_plan_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
2382
  lesson_plan_exam_result_retrun_original = gr.Button("返回原始結果")
2383
+ with gr.Accordion("prompt", open=False) as lesson_plan_accordion:
2384
  lesson_plan_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
2385
  with gr.Column(scale=2):
2386
  # 生成對應不同模式的結果
2387
  lesson_plan_exam_result_prompt = gr.Textbox(visible=False)
2388
  lesson_plan_exam_result_original = gr.Textbox(visible=False)
2389
+ lesson_plan_exam_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
2390
 
2391
  lesson_plan_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
2392
  lesson_plan_exam_result_word_link = gr.File(label="Download Word")
 
2393
  with gr.Tab("出場券"):
2394
  with gr.Row():
2395
  with gr.Column(scale=1):
 
2401
  exit_ticket_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2402
  exit_ticket_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
2403
  exit_ticket_exam_result_retrun_original = gr.Button("返回原始結果")
2404
+ with gr.Accordion("prompt", open=False) as exit_ticket_accordion:
2405
  exit_ticket_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
2406
  with gr.Column(scale=2):
2407
  # 生成對應不同模式的結果
2408
  exit_ticket_exam_result_prompt = gr.Textbox(visible=False)
2409
  exit_ticket_exam_result_original = gr.Textbox(visible=False)
2410
+ exit_ticket_exam_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
2411
 
2412
  exit_ticket_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
2413
  exit_ticket_exam_result_word_link = gr.File(label="Download Word")
 
2431
  with gr.Tab("逐字稿本文"):
2432
  with gr.Row() as transcript_admmin:
2433
  transcript_kind = gr.Textbox(value="transcript", show_label=False)
2434
+ transcript_get_button = gr.Button("取得", size="sm", variant="primary")
2435
  transcript_edit_button = gr.Button("編輯", size="sm", variant="primary")
2436
+ transcript_update_button = gr.Button("儲存", size="sm", variant="primary")
2437
  transcript_delete_button = gr.Button("刪除", size="sm", variant="primary")
2438
  transcript_create_button = gr.Button("建立", size="sm", variant="primary")
2439
  with gr.Row():
2440
  df_string_output = gr.Textbox(lines=40, label="Data Text", interactive=False, show_copy_button=True)
2441
+ with gr.Tab("文章本文"):
2442
+ with gr.Row() as reading_passage_admin:
2443
+ reading_passage_kind = gr.Textbox(value="reading_passage", show_label=False)
2444
+ reading_passage_get_button = gr.Button("取得", size="sm", variant="primary")
2445
+ reading_passage_edit_button = gr.Button("編輯", size="sm", variant="primary")
2446
+ reading_passage_update_button = gr.Button("更新", size="sm", variant="primary")
2447
+ reading_passage_delete_button = gr.Button("刪除", size="sm", variant="primary")
2448
+ reading_passage_create_button = gr.Button("建立", size="sm", variant="primary")
2449
+ with gr.Row():
2450
+ reading_passage_text = gr.Textbox(label="reading_passage", lines=40, interactive=False, show_copy_button=True)
2451
+ with gr.Tab("重點摘要本文"):
2452
+ with gr.Row() as summary_admmin:
2453
+ summary_kind = gr.Textbox(value="summary", show_label=False)
2454
+ summary_get_button = gr.Button("取得", size="sm", variant="primary")
2455
+ summary_edit_button = gr.Button("編輯", size="sm", variant="primary")
2456
+ summary_update_button = gr.Button("更新", size="sm", variant="primary")
2457
+ summary_delete_button = gr.Button("刪除", size="sm", variant="primary")
2458
+ summary_create_button = gr.Button("建立", size="sm", variant="primary")
2459
+ with gr.Row():
2460
+ summary_text = gr.Textbox(label="Summary", lines=40, interactive=False, show_copy_button=True)
2461
  with gr.Tab("關鍵時刻本文"):
2462
  with gr.Row() as key_moments_admin:
2463
  key_moments_kind = gr.Textbox(value="key_moments", show_label=False)
2464
+ key_moments_get_button = gr.Button("取得", size="sm", variant="primary")
2465
  key_moments_edit_button = gr.Button("編輯", size="sm", variant="primary")
2466
+ key_moments_update_button = gr.Button("儲存", size="sm", variant="primary")
2467
  key_moments_delete_button = gr.Button("刪除", size="sm", variant="primary")
2468
  key_moments_create_button = gr.Button("建立", size="sm", variant="primary")
2469
  with gr.Row():
 
2471
  with gr.Tab("問題本文"):
2472
  with gr.Row() as question_list_admin:
2473
  questions_kind = gr.Textbox(value="questions", show_label=False)
2474
+ questions_get_button = gr.Button("取得", size="sm", variant="primary")
2475
  questions_edit_button = gr.Button("編輯", size="sm", variant="primary")
2476
+ questions_update_button = gr.Button("儲存", size="sm", variant="primary")
2477
  questions_delete_button = gr.Button("刪除", size="sm", variant="primary")
2478
  questions_create_button = gr.Button("建立", size="sm", variant="primary")
2479
  with gr.Row():
 
2497
  mind_map_html = gr.HTML()
2498
 
2499
  # --- Event ---
2500
+ # CHATBOT SELECT
2501
+ chatbot_open_ai_select_btn.click(
2502
+ chatbot_select,
2503
+ inputs=[chatbot_open_ai_name],
2504
+ outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
2505
+ )
2506
+ chatbot_open_ai_streaming_select_btn.click(
2507
+ chatbot_select,
2508
+ inputs=[chatbot_open_ai_streaming_name],
2509
+ outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
2510
+ )
2511
+ chatbot_jutor_select_btn.click(
2512
+ chatbot_select,
2513
+ inputs=[chatbot_jutor_name],
2514
+ outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
2515
+ )
2516
+
2517
  # OPENAI ASSISTANT CHATBOT 模式
2518
  send_button.click(
2519
  chat_with_opan_ai_assistant,
2520
+ inputs=[password, video_id, user_data, thread_id, trascript_state, key_moments, msg, chatbot, content_subject, content_grade, socratic_mode_btn],
2521
+ outputs=[msg, chatbot, thread_id],
2522
+ scroll_to_output=True
2523
  )
2524
  openai_chatbot_audio_input.change(
2525
  process_open_ai_audio_to_chatbot,
 
2527
  outputs=[msg]
2528
  )
2529
  # OPENAI ASSISTANT CHATBOT 連接按鈕點擊事件
2530
+ 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]
2531
+ 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]
2532
+ 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]
2533
  btn_1.click(
2534
  chat_with_opan_ai_assistant,
2535
  inputs=btn_1_chat_with_opan_ai_assistant_input,
2536
+ outputs=[msg, chatbot, thread_id],
2537
+ scroll_to_output=True
2538
  )
2539
  btn_2.click(
2540
  chat_with_opan_ai_assistant,
2541
  inputs=btn_2_chat_with_opan_ai_assistant_input,
2542
+ outputs=[msg, chatbot, thread_id],
2543
+ scroll_to_output=True
2544
  )
2545
  btn_3.click(
2546
  chat_with_opan_ai_assistant,
2547
  inputs=btn_3_chat_with_opan_ai_assistant_input,
2548
+ outputs=[msg, chatbot, thread_id],
2549
+ scroll_to_output=True
2550
  )
2551
  btn_create_question.click(
2552
  change_questions,
 
2554
  outputs = [btn_1, btn_2, btn_3]
2555
  )
2556
 
2557
+ # 其他精靈 ai_chatbot 模式
2558
  ai_send_button.click(
2559
  chat_with_ai,
2560
+ 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],
2561
+ outputs=[ai_msg, ai_chatbot],
2562
+ scroll_to_output=True
2563
  )
2564
+ # 其他精靈 ai_chatbot 连接按钮点击事件
2565
+ 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]
2566
+ 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]
2567
+ 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]
2568
  ai_chatbot_question_1.click(
2569
  chat_with_ai,
2570
  inputs=ai_chatbot_question_1_chat_with_ai_input,
2571
+ outputs=[ai_msg, ai_chatbot],
2572
+ scroll_to_output=True
2573
  )
2574
  ai_chatbot_question_2.click(
2575
  chat_with_ai,
2576
  inputs=ai_chatbot_question_2_chat_with_ai_input,
2577
+ outputs=[ai_msg, ai_chatbot],
2578
+ scroll_to_output=True
2579
  )
2580
  ai_chatbot_question_3.click(
2581
  chat_with_ai,
2582
  inputs=ai_chatbot_question_3_chat_with_ai_input,
2583
+ outputs=[ai_msg, ai_chatbot],
2584
+ scroll_to_output=True
2585
  )
2586
 
2587
  # file_upload.change(process_file, inputs=file_upload, outputs=df_string_output)
 
2596
  btn_2,
2597
  btn_3,
2598
  df_string_output,
2599
+ summary_text,
2600
  df_summarise,
2601
  key_moments,
2602
  key_moments_html,
 
2606
  simple_html_content,
2607
  slide_image,
2608
  slide_text,
2609
+ reading_passage_text,
2610
  reading_passage,
2611
  content_subject,
2612
  content_grade,
 
2656
  # web_link.change(process_web_link, inputs=web_link, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
2657
 
2658
  # reading_passage event
2659
+ reading_passage_get_button.click(
2660
+ get_LLM_content,
2661
+ inputs=[video_id, reading_passage_kind],
2662
+ outputs=[reading_passage_text]
2663
+ )
2664
  reading_passage_create_button.click(
2665
  create_LLM_content,
2666
  inputs=[video_id, df_string_output, reading_passage_kind],
2667
+ outputs=[reading_passage_text]
2668
  )
2669
  reading_passage_delete_button.click(
2670
  delete_LLM_content,
2671
  inputs=[video_id, reading_passage_kind],
2672
+ outputs=[reading_passage_text]
2673
  )
2674
  reading_passage_edit_button.click(
2675
  enable_edit_mode,
2676
  inputs=[],
2677
+ outputs=[reading_passage_text]
2678
  )
2679
  reading_passage_update_button.click(
2680
  update_LLM_content,
2681
+ inputs=[video_id, reading_passage_text, reading_passage_kind],
2682
+ outputs=[reading_passage_text]
2683
  )
2684
 
2685
  # summary event
2686
+ summary_get_button.click(
2687
+ get_LLM_content,
2688
+ inputs=[video_id, summary_kind],
2689
+ outputs=[summary_text]
2690
+ )
2691
  summary_create_button.click(
2692
  create_LLM_content,
2693
  inputs=[video_id, df_string_output, summary_kind],
2694
+ outputs=[summary_text]
2695
  )
2696
  summary_delete_button.click(
2697
  delete_LLM_content,
2698
  inputs=[video_id, summary_kind],
2699
+ outputs=[summary_text]
2700
  )
2701
  summary_edit_button.click(
2702
  enable_edit_mode,
2703
  inputs=[],
2704
+ outputs=[summary_text]
2705
  )
2706
  summary_update_button.click(
2707
  update_LLM_content,
2708
+ inputs=[video_id, summary_text, summary_kind],
2709
+ outputs=[summary_text]
2710
  )
2711
 
2712
  # transcript event
2713
+ transcript_get_button.click(
2714
+ get_LLM_content,
2715
+ inputs=[video_id, transcript_kind],
2716
+ outputs=[df_string_output]
2717
+ )
2718
  transcript_create_button.click(
2719
  create_LLM_content,
2720
  inputs=[video_id, df_string_output, transcript_kind],
 
2737
  )
2738
 
2739
  # key_moments event
2740
+ key_moments_get_button.click(
2741
+ get_LLM_content,
2742
+ inputs=[video_id, key_moments_kind],
2743
+ outputs=[key_moments]
2744
+ )
2745
  key_moments_create_button.click(
2746
  create_LLM_content,
2747
  inputs=[video_id, df_string_output, key_moments_kind],
 
2764
  )
2765
 
2766
  # question_list event
2767
+ questions_get_button.click(
2768
+ get_LLM_content,
2769
+ inputs=[video_id, questions_kind],
2770
+ outputs=[questions_json]
2771
+ )
2772
  questions_create_button.click(
2773
  create_LLM_content,
2774
  inputs=[video_id, df_string_output, questions_kind],
 
2855
  )
2856
 
2857
  # init_params
2858
+ init_outputs = [
2859
+ admin,
2860
+ reading_passage_admin,
2861
+ summary_admmin,
2862
+ see_details,
2863
+ worksheet_accordion,
2864
+ lesson_plan_accordion,
2865
+ exit_ticket_accordion,
2866
+ password,
2867
+ youtube_link,
2868
+ chatbot_open_ai,
2869
+ chatbot_open_ai_streaming,
2870
+ chatbot_jutor
2871
+ ]
2872
  demo.load(
2873
  init_params,
2874
  inputs =[youtube_link],
2875
+ outputs = init_outputs
2876
  )
2877
 
2878
  demo.launch(allowed_paths=["videos"])
chatbot.py CHANGED
@@ -82,7 +82,7 @@ class Chatbot:
82
  "Content-Type": "application/json",
83
  "x-api-key": self.jutor_chat_key,
84
  }
85
- model = "gpt-4-1106-preview"
86
  # model = "gpt-3.5-turbo-0125"
87
  data = {
88
  "data": {
 
82
  "Content-Type": "application/json",
83
  "x-api-key": self.jutor_chat_key,
84
  }
85
+ model = "gpt-4-turbo"
86
  # model = "gpt-3.5-turbo-0125"
87
  data = {
88
  "data": {
educational_material.py CHANGED
@@ -30,14 +30,13 @@ class EducationalMaterial:
30
  self.level = level
31
  self.specific_feature = specific_feature
32
  self.content_type = content_type # 'worksheet' or 'lesson_plan'
33
- self.system_content = "你是一個擅長資料分析跟影片教學備課的老師,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
34
 
35
  def _prepare_context(self, context):
36
  context_json = json.loads(context)
 
37
  for entry in context_json:
38
- entry.pop('embed_url', None)
39
- entry.pop('screenshot_path', None)
40
- processed_context = json.dumps(context_json, ensure_ascii=False, indent=2)
41
  return processed_context
42
 
43
  def generate_content_prompt(self):
@@ -50,7 +49,8 @@ class EducationalMaterial:
50
 
51
  def _generate_worksheet_prompt(self):
52
  bloom_worksheet_prompt = """
53
- 你是個專業的教師,熟悉布魯姆(Benjamin Bloom, 1964) 的認知理論。布魯姆認為人類的能力,大致可分為三個領域(domains),即認知領域(cognitive domain)、情意領域(affective domain)、技能領域 (psychomotor domain)。
 
54
 
55
  【認知領域】涉及知能及其運作,著重心智、學習以及問題解決的工作。認知目標從簡單的認識或記憶能力到複雜的評鑑能力。大部分的教育目標都屬於這個領域。認知領域的目標分為六個層次,每個層次代表不同的心智功能。
56
  - 📖 知識:在認知目標中知識是最低層次的能力,包括名詞、事實、規則和原理原則等的認識和記憶。用來表示此種能力的行為動詞有:指出、寫出、界定、說明、舉例、命名、認明等。例:能在地圖上指出長江流經的省分。
@@ -74,12 +74,12 @@ class EducationalMaterial:
74
  🌐【主題】:認識公里
75
 
76
  🎓【對象】
77
- 科目: 數學
78
- 年級: 三年級
79
- 難度: 基礎
80
 
81
  🏞️【情境描述】
82
- 狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
83
 
84
  🔑【影片重點】
85
  - 公里是用來測量長距離的單位,通常用於測量很遠的距離。
@@ -101,7 +101,8 @@ class EducationalMaterial:
101
  """
102
 
103
  Polya_worksheet_prompt = """
104
- 你是個專業的教師,熟悉 George Polya(1945) 的數學問題解決策略。
 
105
  Polya提出了一個四步驟的數學問題解決策略,在他影響深遠的經典著作 How to solve it《如何解題》中指出解難過程可分為四個階段:
106
  (1) 理解問題 (understanding the problem)
107
  (2) 設計解題策略 (devising a plan)
@@ -115,9 +116,9 @@ class EducationalMaterial:
115
  🌐 主題:【概念】認識公里
116
 
117
  🎓【對象】
118
- 科目: 數學
119
- 年級: 三年級
120
- 難度: 基礎
121
 
122
  🏞️【情境描述】
123
  狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
@@ -148,7 +149,8 @@ class EducationalMaterial:
148
 
149
  # CRA教學法
150
  cra_worksheet_prompt = """
151
- 你是個專業的教師,熟悉CRA教學法,CRA教學法是一種教學策略,
 
152
  CRA正是一種用來促進學生學習和記憶數學的三步教學法,它闡明瞭用這種方式進行教學的具體步驟。
153
  CRA的三個步驟互相依賴,運用CRA能建立起一種概念結構,從而形成知識的意義關聯。
154
  CRA策略的第一個階段, 即實例(C)階段,是一個“做”的階段。在這一階段,教師用加工材料建模,這些材料包括彩色圓片、立方體、十進位積木、六形六色積木,以及分數積木,等等。在使用這些材料時,必須考���到兒童的視覺、觸覺及動感經驗。
@@ -160,9 +162,9 @@ class EducationalMaterial:
160
  🌐 主題:【概念】認識公里
161
 
162
  🎓【對象】
163
- 科目: 數學
164
- 年級: 三年級
165
- 難度: 基礎
166
 
167
  【實例(C)階段】
168
  1. 用彩色圓片來解釋什麼是分數?
 
30
  self.level = level
31
  self.specific_feature = specific_feature
32
  self.content_type = content_type # 'worksheet' or 'lesson_plan'
33
+ self.system_content = "你是一個擅長資料分析跟影片教學備課的老師,請精讀資料文本,自行判斷資料的種類,使用 zh-TW,遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。"
34
 
35
  def _prepare_context(self, context):
36
  context_json = json.loads(context)
37
+ processed_context = ""
38
  for entry in context_json:
39
+ processed_context += entry.get('text', '') + "\n"
 
 
40
  return processed_context
41
 
42
  def generate_content_prompt(self):
 
49
 
50
  def _generate_worksheet_prompt(self):
51
  bloom_worksheet_prompt = """
52
+ 你是個專業的教師,,遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。
53
+ 熟悉布魯姆(Benjamin Bloom, 1964) 的認知理論。布魯姆認為人類的能力,大致可分為三個領域(domains),即認知領域(cognitive domain)、情意領域(affective domain)、技能領域 (psychomotor domain)。
54
 
55
  【認知領域】涉及知能及其運作,著重心智、學習以及問題解決的工作。認知目標從簡單的認識或記憶能力到複雜的評鑑能力。大部分的教育目標都屬於這個領域。認知領域的目標分為六個層次,每個層次代表不同的心智功能。
56
  - 📖 知識:在認知目標中知識是最低層次的能力,包括名詞、事實、規則和原理原則等的認識和記憶。用來表示此種能力的行為動詞有:指出、寫出、界定、說明、舉例、命名、認明等。例:能在地圖上指出長江流經的省分。
 
74
  🌐【主題】:認識公里
75
 
76
  🎓【對象】
77
+ - 科目: 數學
78
+ - 年級: 三年級
79
+ - 難度: 基礎
80
 
81
  🏞️【情境描述】
82
+ - 狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
83
 
84
  🔑【影片重點】
85
  - 公里是用來測量長距離的單位,通常用於測量很遠的距離。
 
101
  """
102
 
103
  Polya_worksheet_prompt = """
104
+ 你是個專業的教師,遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。
105
+ 熟悉 George Polya(1945) 的數學問題解決策略。
106
  Polya提出了一個四步驟的數學問題解決策略,在他影響深遠的經典著作 How to solve it《如何解題》中指出解難過程可分為四個階段:
107
  (1) 理解問題 (understanding the problem)
108
  (2) 設計解題策略 (devising a plan)
 
116
  🌐 主題:【概念】認識公里
117
 
118
  🎓【對象】
119
+ - 科目: 數學
120
+ - 年級: 三年級
121
+ - 難度: 基礎
122
 
123
  🏞️【情境描述】
124
  狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
 
149
 
150
  # CRA教學法
151
  cra_worksheet_prompt = """
152
+ 你是個專業的教師,,遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。
153
+ 熟悉CRA教學法,CRA教學法是一種教學策略,
154
  CRA正是一種用來促進學生學習和記憶數學的三步教學法,它闡明瞭用這種方式進行教學的具體步驟。
155
  CRA的三個步驟互相依賴,運用CRA能建立起一種概念結構,從而形成知識的意義關聯。
156
  CRA策略的第一個階段, 即實例(C)階段,是一個“做”的階段。在這一階段,教師用加工材料建模,這些材料包括彩色圓片、立方體、十進位積木、六形六色積木,以及分數積木,等等。在使用這些材料時,必須考���到兒童的視覺、觸覺及動感經驗。
 
162
  🌐 主題:【概念】認識公里
163
 
164
  🎓【對象】
165
+ - 科目: 數學
166
+ - 年級: 三年級
167
+ - 難度: 基礎
168
 
169
  【實例(C)階段】
170
  1. 用彩色圓片來解釋什麼是分數?