youngtsai commited on
Commit
aaa2911
1 Parent(s): 4ab6d4c
Files changed (1) hide show
  1. app.py +331 -373
app.py CHANGED
@@ -694,6 +694,85 @@ def screenshot_youtube_video(youtube_id, snapshot_sec):
694
 
695
 
696
  # ---- LLM Generator ----
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
  def get_reading_passage(video_id, df_string, source):
698
  if source == "gcs":
699
  print("===get_reading_passage on gcs===")
@@ -738,62 +817,30 @@ def get_reading_passage(video_id, df_string, source):
738
  return reading_passage_json
739
 
740
  def generate_reading_passage(df_string):
741
- # 使用 OpenAI 生成基于上传数据的问题
742
- sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
743
- user_content = f"""
744
- 請根據 {df_string}
745
- 文本自行判斷資料的種類
746
- 幫我組合成 Reading Passage
747
- 並潤稿讓文句通順
748
- 請一定要使用繁體中文 zh-TW,並用台灣人的口語
749
- 產生的結果不要前後文解釋,也不要敘述這篇文章怎麼產生的
750
- 只需要專注提供 Reading Passage,字數在 500 字以內
751
- 敘述中,請把數學或是專業術語,用 Latex 包覆($...$),並且不要去改原本的文章
752
- 加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
753
- """
754
-
755
- try:
756
- # 使用 OPEN AI 生成 Reading Passage
757
- messages = [
758
- {"role": "system", "content": sys_content},
759
- {"role": "user", "content": user_content}
760
- ]
761
-
762
- request_payload = {
763
- "model": "gpt-4-turbo",
764
- "messages": messages,
765
- "max_tokens": 4000,
766
- }
767
-
768
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
769
- reading_passage = response.choices[0].message.content.strip()
770
- except:
771
- # 使用 REDROCK 生成 Reading Passage
772
- messages = [
773
- {"role": "user", "content": user_content}
774
- ]
775
- model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
776
- # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
777
- kwargs = {
778
- "modelId": model_id,
779
- "contentType": "application/json",
780
- "accept": "application/json",
781
- "body": json.dumps({
782
- "anthropic_version": "bedrock-2023-05-31",
783
- "max_tokens": 4000,
784
- "system": sys_content,
785
- "messages": messages
786
- })
787
- }
788
- response = BEDROCK_CLIENT.invoke_model(**kwargs)
789
- response_body = json.loads(response.get('body').read())
790
- reading_passage = response_body.get('content')[0].get('text')
791
-
792
- print("=====reading_passage=====")
793
- print(reading_passage)
794
- print("=====reading_passage=====")
795
-
796
- return reading_passage
797
 
798
  def text_to_speech(video_id, text):
799
  tts = gTTS(text, lang='en')
@@ -846,55 +893,23 @@ def get_mind_map(video_id, df_string, source):
846
  return mind_map_json
847
 
848
  def generate_mind_map(df_string):
849
- # 使用 OpenAI 生成基于上传数据的问题
850
- sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
851
- user_content = f"""
852
- 請根據 {df_string} 文本建立 markdown 心智圖
853
- 注意:不需要前後文敘述,直接給出 markdown 文本即可
854
- 這對我很重要
855
- """
856
-
857
- try:
858
- # 使用 OPEN AI 生成
859
- messages = [
860
- {"role": "system", "content": sys_content},
861
- {"role": "user", "content": user_content}
862
- ]
863
-
864
- request_payload = {
865
- "model": "gpt-4-turbo",
866
- "messages": messages,
867
- "max_tokens": 4000,
868
- }
869
-
870
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
871
- mind_map = response.choices[0].message.content.strip()
872
- except:
873
- # 使用 REDROCK 生成
874
- messages = [
875
- {"role": "user", "content": user_content}
876
- ]
877
- model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
878
- # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
879
- kwargs = {
880
- "modelId": model_id,
881
- "contentType": "application/json",
882
- "accept": "application/json",
883
- "body": json.dumps({
884
- "anthropic_version": "bedrock-2023-05-31",
885
- "max_tokens": 4000,
886
- "system": sys_content,
887
- "messages": messages
888
- })
889
- }
890
- response = BEDROCK_CLIENT.invoke_model(**kwargs)
891
- response_body = json.loads(response.get('body').read())
892
- mind_map = response_body.get('content')[0].get('text')
893
- print("=====mind_map=====")
894
- print(mind_map)
895
- print("=====mind_map=====")
896
 
897
- return mind_map
 
 
898
 
899
  def get_mind_map_html(mind_map):
900
  mind_map_markdown = mind_map.replace("```markdown", "").replace("```", "")
@@ -963,6 +978,7 @@ def get_video_id_summary(video_id, df_string, source):
963
  return summary_json
964
 
965
  def generate_summarise(df_string, metadata=None):
 
966
  # 使用 OpenAI 生成基于上传数据的问题
967
  if metadata:
968
  title = metadata.get("title", "")
@@ -973,89 +989,86 @@ def generate_summarise(df_string, metadata=None):
973
  subject = ""
974
  grade = ""
975
 
976
- sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
977
- user_content = f"""
978
- 課程名稱:{title}
979
- 科目:{subject}
980
- 年級:{grade}
981
 
982
- 請根據內文: {df_string}
983
-
984
- 格式為 Markdown
985
- 如果有課程名稱,請圍繞「課程名稱」為學習重點,進行重點整理,不要整理跟情境故事相關的問題
986
- 整體摘要在一百字以內
987
- 重點概念列出 bullet points,至少三個,最多五個
988
- 以及可能的結論與結尾延伸小問題提供學生作反思
989
- 敘述中,請把數學或是專業術語,用 Latex 包覆($...$)
990
- 加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
991
 
992
- 整體格式為:
993
- ## 🌟 主題:{{title}} (如果沒有 title 就省略)
994
- ## 📚 整體摘要
995
- - (一個 bullet point....)
996
-
997
- ## 🔖 重點概念
998
- - xxx
999
- - xxx
1000
- - xxx
1001
-
1002
- ## 💡 為什麼我們要學這個?
1003
- - (一個 bullet point....)
1004
-
1005
- ## 延伸小問題
1006
- - (一個 bullet point....請圍繞「課程名稱」為學習重點,進行重點整理,不要整理跟情境故事相關的問題)
1007
- """
1008
-
1009
- # 🗂️ 1. 內容類型:?
1010
- # 📚 2. 整體摘要
1011
- # 🔖 3. 條列式重點
1012
- # 🔑 4. 關鍵時刻(段落摘要)
1013
- # 💡 5. 結論反思(為什麼我們要學這個?)
1014
- # ❓ 6. 延伸小問題
1015
-
1016
- try:
1017
- #OPEN AI
1018
- messages = [
1019
- {"role": "system", "content": sys_content},
1020
- {"role": "user", "content": user_content}
1021
- ]
1022
-
1023
- request_payload = {
1024
- "model": "gpt-4-turbo",
1025
- "messages": messages,
1026
- "max_tokens": 4000,
1027
- }
1028
-
1029
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1030
- df_summarise = response.choices[0].message.content.strip()
1031
- except:
1032
- #REDROCK
1033
- messages = [
1034
- {"role": "user", "content": user_content}
1035
- ]
1036
- model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
1037
- # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
1038
- kwargs = {
1039
- "modelId": model_id,
1040
- "contentType": "application/json",
1041
- "accept": "application/json",
1042
- "body": json.dumps({
1043
- "anthropic_version": "bedrock-2023-05-31",
1044
- "max_tokens": 4000,
1045
- "system": sys_content,
1046
- "messages": messages
1047
- })
1048
- }
1049
- response = BEDROCK_CLIENT.invoke_model(**kwargs)
1050
- response_body = json.loads(response.get('body').read())
1051
- df_summarise = response_body.get('content')[0].get('text')
1052
-
1053
-
1054
- print("=====df_summarise=====")
1055
- print(df_summarise)
1056
- print("=====df_summarise=====")
 
 
 
1057
 
1058
- return df_summarise
1059
 
1060
  def get_questions(video_id, df_string, source="gcs"):
1061
  if source == "gcs":
@@ -1110,6 +1123,7 @@ def get_questions(video_id, df_string, source="gcs"):
1110
  return q1, q2, q3
1111
 
1112
  def generate_questions(df_string):
 
1113
  # 使用 OpenAI 生成基于上传数据的问题
1114
  if isinstance(df_string, str):
1115
  df_string_json = json.loads(df_string)
@@ -1121,9 +1135,19 @@ def generate_questions(df_string):
1121
  content_text += entry["text"] + ","
1122
 
1123
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,並用既有資料為本質猜測用戶可能會問的問題,使用 zh-TW"
1124
- user_content = f"請根據 {content_text} 生成三個問題,並用 JSON 格式返回 questions:[q1的敘述text, q2的敘述text, q3的敘述text]"
 
 
 
 
 
 
 
 
 
1125
 
1126
  try:
 
1127
  messages = [
1128
  {"role": "system", "content": sys_content},
1129
  {"role": "user", "content": user_content}
@@ -1136,7 +1160,7 @@ def generate_questions(df_string):
1136
 
1137
 
1138
  request_payload = {
1139
- "model": "gpt-4-turbo",
1140
  "messages": messages,
1141
  "max_tokens": 4000,
1142
  "response_format": response_format
@@ -1192,69 +1216,48 @@ def get_questions_answers(video_id, df_string, source="gcs"):
1192
  print("questions_answers已存在于GCS中")
1193
  questions_answers_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1194
  questions_answers = json.loads(questions_answers_text)
1195
- except:
 
1196
  questions = get_questions(video_id, df_string, source)
1197
  questions_answers = [{"question": q, "answer": ""} for q in questions]
1198
 
1199
  return questions_answers
1200
 
1201
  def generate_questions_answers(df_string):
1202
- content_text = str(df_string)
1203
- sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,並用既有資料為本質猜測用戶可能會問的問題,使用 zh-TW"
1204
- user_content = f"""
1205
- 請根據 {content_text} 生成三個問題跟答案,主要與學科有關,不要問跟情節故事相關的問題
1206
- 答案要在最後標示出處【參考:00:01:05】,請根據時間軸 start_time 來標示
1207
- 請確保問題跟答案都是繁體中文 zh-TW
1208
- 答案不用是標準答案,而是帶有啟發性的蘇格拉底式問答,讓學生思考本來的問題,以及該去參考的時間點
1209
- 並用 JSON 格式返回 questions_answers: [{{question: q1的敘述text, answer: q1的答案text}}, ...]
1210
- k-v pair 的 key 是 question, value 是 answer
1211
- """
1212
-
1213
- try:
1214
- # OPENAI
1215
- messages = [
1216
- {"role": "system", "content": sys_content},
1217
- {"role": "user", "content": user_content}
1218
- ]
 
 
 
 
 
 
 
1219
  response_format = { "type": "json_object" }
1220
- request_payload = {
1221
- "model": "gpt-4-turbo",
1222
- "messages": messages,
1223
- "max_tokens": 4000,
1224
- "response_format": response_format
1225
- }
1226
-
1227
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1228
- questions_answers = json.loads(response.choices[0].message.content)["questions_answers"]
1229
- except:
1230
- # REDROCK_CLIENT
1231
- messages = [
1232
- {"role": "user", "content": user_content}
1233
- ]
1234
- model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
1235
- # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
1236
- kwargs = {
1237
- "modelId": model_id,
1238
- "contentType": "application/json",
1239
- "accept": "application/json",
1240
- "body": json.dumps({
1241
- "anthropic_version": "bedrock-2023-05-31",
1242
- "max_tokens": 4000,
1243
- "system": sys_content,
1244
- "messages": messages
1245
- })
1246
- }
1247
- response = BEDROCK_CLIENT.invoke_model(**kwargs)
1248
- response_body = json.loads(response.get('body').read())
1249
- response_completion = response_body.get('content')[0].get('text')
1250
- questions_answers = json.loads(response_completion)["questions_answers"]
1251
 
1252
- print("=====json_response=====")
1253
- print(questions_answers)
1254
- print("=====json_response=====")
1255
-
1256
- return questions_answers
1257
 
 
1258
 
1259
  def change_questions(password, df_string):
1260
  verify_password(password)
@@ -1331,6 +1334,7 @@ def get_key_moments(video_id, formatted_simple_transcript, formatted_transcript,
1331
  return key_moments_json
1332
 
1333
  def generate_key_moments(formatted_simple_transcript, formatted_transcript):
 
1334
  # 使用 OpenAI 生成基于上传数据的问题
1335
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
1336
  user_content = f"""
@@ -1341,7 +1345,7 @@ def generate_key_moments(formatted_simple_transcript, formatted_transcript):
1341
  4. 如果頭尾的情節不是重點,特別是打招呼或是介紹人物、或是say goodbye 就是不重要的情節,就不用擷取
1342
  5. 以這種方式分析整個文本,從零秒開始分析,直到結束。這很重要
1343
  6. 關鍵字從transcript extract to keyword,保留專家名字、專業術語、年份、數字、期刊名稱、地名、數學公式
1344
- 7. text, keywords please use or transfer zh-TW, it's very important
1345
 
1346
  Example: retrun JSON
1347
  {{key_moments:[{{
@@ -1353,124 +1357,77 @@ def generate_key_moments(formatted_simple_transcript, formatted_transcript):
1353
  }}
1354
  """
1355
 
1356
- try:
1357
- #OPEN AI
1358
- messages = [
1359
- {"role": "system", "content": sys_content},
1360
- {"role": "user", "content": user_content}
1361
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1362
  response_format = { "type": "json_object" }
1363
-
1364
- request_payload = {
1365
- "model": "gpt-4-turbo",
1366
- "messages": messages,
1367
- "max_tokens": 4096,
1368
- "response_format": response_format
1369
- }
1370
-
1371
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1372
- print("===response===")
1373
- print(dict(response))
1374
- key_moments = json.loads(response.choices[0].message.content)["key_moments"]
1375
- except Exception as e:
1376
- error_msg = f" {video_id} OPEN AI 關鍵時刻錯誤: {str(e)}"
1377
- print("===generate_key_moments error===")
1378
- print(error_msg)
1379
- print("===generate_key_moments error===")
1380
-
1381
- #REDROCK
1382
- messages = [
1383
- {"role": "user", "content": user_content}
1384
- ]
1385
- model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
1386
- # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
1387
- kwargs = {
1388
- "modelId": model_id,
1389
- "contentType": "application/json",
1390
- "accept": "application/json",
1391
- "body": json.dumps({
1392
- "anthropic_version": "bedrock-2023-05-31",
1393
- "max_tokens": 4096,
1394
- "system": sys_content,
1395
- "messages": messages
1396
- })
1397
- }
1398
- response = BEDROCK_CLIENT.invoke_model(**kwargs)
1399
- response_body = json.loads(response.get('body').read())
1400
- response_completion = response_body.get('content')[0].get('text')
1401
- print(f"response_completion: {response_completion}")
1402
-
1403
- key_moments = json.loads(response_completion)["key_moments"]
1404
-
1405
- # "transcript": get text from formatted_simple_transcript
1406
- for moment in key_moments:
1407
- start_time = parse_time(moment['start'])
1408
- end_time = parse_time(moment['end'])
1409
- # 使用轉換後的 timedelta 物件進行時間
1410
- moment['transcript'] = ",".join([entry['text'] for entry in formatted_simple_transcript
1411
- if start_time <= parse_time(entry['start_time']) <= end_time])
1412
-
1413
- print("=====key_moments=====")
1414
- print(key_moments)
1415
- print("=====key_moments=====")
1416
- image_links = {entry['start_time']: entry['screenshot_path'] for entry in formatted_transcript}
1417
-
1418
- for moment in key_moments:
1419
- start_time = parse_time(moment['start'])
1420
- end_time = parse_time(moment['end'])
1421
- # 使用轉換後的 timedelta 物件進行時間比較
1422
- moment_images = [image_links[time] for time in image_links
1423
- if start_time <= parse_time(time) <= end_time]
1424
- moment['images'] = moment_images
1425
-
1426
- return key_moments
1427
 
1428
  def generate_key_moments_keywords(transcript):
1429
- system_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請根據以下文本提取關鍵字"
1430
- user_content = f"""transcript extract to keyword
1431
- 保留專家名字、專業術語、年份、數字、期刊名稱、地名、數學公式、數學表示式、物理化學符號,
1432
- 不用給上下文,直接給出關鍵字,使用 zh-TW,用逗號分隔, example: 關鍵字1, 關鍵字2
1433
- transcript:{transcript}
1434
- """
1435
-
1436
- try:
1437
- # OPEN AI
1438
- messages = [
1439
- {"role": "system", "content": system_content},
1440
- {"role": "user", "content": user_content}
1441
- ]
1442
- request_payload = {
1443
- "model": "gpt-4-turbo",
1444
- "messages": messages,
1445
- "max_tokens": 100,
1446
- }
1447
-
1448
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1449
- keywords = response.choices[0].message.content.strip().split(", ")
1450
- except:
1451
- # REDROCK
1452
- messages = [
1453
- {"role": "user", "content": user_content}
1454
- ]
1455
- model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
1456
- # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
1457
- kwargs = {
1458
- "modelId": model_id,
1459
- "contentType": "application/json",
1460
- "accept": "application/json",
1461
- "body": json.dumps({
1462
- "anthropic_version": "bedrock-2023-05-31",
1463
- "max_tokens": 100,
1464
- "system": system_content,
1465
- "messages": messages
1466
- })
1467
- }
1468
- response = BEDROCK_CLIENT.invoke_model(**kwargs)
1469
- response_body = json.loads(response.get('body').read())
1470
- response_completion = response_body.get('content')[0].get('text')
1471
- keywords = response_completion.strip().split(", ")
1472
 
1473
- return keywords
1474
 
1475
  def get_key_moments_html(key_moments):
1476
  css = """
@@ -2817,12 +2774,11 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2817
  with gr.Column(scale=1, variant="panel"):
2818
  foxcat_chatbot_avatar_url = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2020/06/%E7%A7%91%E5%AD%B8%E5%BE%BD%E7%AB%A0-2-150x150.png"
2819
  foxcat_avatar_images = gr.State([user_avatar, foxcat_chatbot_avatar_url])
2820
- foxcat_chatbot_description = """Hi,我是【狐狸貓】,\n
2821
- 也可以陪你一起學習本次的內容,有什麼問題都可以問我喔!\n
2822
- 🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!\n
2823
- 🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!\n
2824
- 🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!\n
2825
- 💤 精靈們體力都有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!
2826
  """
2827
  foxcat_chatbot_name = gr.State("foxcat")
2828
  gr.Image(value=foxcat_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
@@ -2833,12 +2789,15 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2833
  with gr.Column(scale=1, variant="panel"):
2834
  lili_chatbot_avatar_url = "https://junyitopicimg.s3.amazonaws.com/live/v1283-new-topic-44-icon.png?v=20230529071206714"
2835
  lili_avatar_images = gr.State([user_avatar, lili_chatbot_avatar_url])
2836
- lili_chatbot_description = """你好,我是溫柔的【梨梨】, \n
2837
- 很高興可以在這裡陪伴你學習。如果你有任何疑問,請隨時向我提出哦! \n
2838
- 🤔 如果你在思考如何提問,可以嘗試點擊下方的「問題一」、「問題二」或「問題三」,我會為你生成一些問題來幫助你啟動思考。 \n
2839
- 🗣️ 你也可以使用右下角的語音輸入功能,讓我幫你將語音轉化為文字,這樣可以更加方便快捷。\n
2840
- 🔠 當然,你也可以直接通過鍵盤輸入你的問題,我將盡我所能為你提供答案。\n
2841
- 💤 請理解,即使是我們這些精靈,也有疲憊的時候,每次學習後我能回答的問題有限。如果達到上限,讓我稍作休息之後再繼續回答你的問題吧!
 
 
 
2842
  """
2843
  lili_chatbot_name = gr.State("lili")
2844
  gr.Image(value=lili_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
@@ -2849,12 +2808,11 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2849
  with gr.Column(scale=1, variant="panel"):
2850
  maimai_chatbot_avatar_url = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2020/07/%E6%80%9D%E8%80%83%E5%8A%9B%E8%B6%85%E4%BA%BA%E5%BE%BD%E7%AB%A0_%E5%B7%A5%E4%BD%9C%E5%8D%80%E5%9F%9F-1-%E8%A4%87%E6%9C%AC-150x150.png"
2851
  maimai_avatar_images = gr.State([user_avatar, maimai_chatbot_avatar_url])
2852
- maimai_chatbot_description = """Hi,我是迷人的【麥麥】,\n
2853
- 我在這裡等著和你一起探索新知,任何疑問都可以向我提出!\n
2854
- 🤔 如果你不知道從哪裡開始,試試左下方的「問題一」、「問題二」、「問題三」,我會為你提供一些啟發思考的問題。\n
2855
- 🗣️ 你也可以利用右下角的語音輸入功能,讓我將你的語音轉成文字,是不是很酷?\n
2856
- 🔠 當然,你也可以直接透過鍵盤向我發問,我會全力以赴來回答你的每一個問題。\n
2857
- 💤 我們這些精靈也需要休息,每次學習我們只能回答十個問題,當達到上限時,請給我一點時間充電再繼續。
2858
  """
2859
  maimai_chatbot_name = gr.State("maimai")
2860
  gr.Image(value=maimai_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
 
694
 
695
 
696
  # ---- LLM Generator ----
697
+ def split_data(df_string, word_base=100000):
698
+ """Split the JSON string based on a character length base and then chunk the parsed JSON array."""
699
+ if isinstance(df_string, str):
700
+ data_str_cnt = len(df_string)
701
+ data = json.loads(df_string)
702
+ else:
703
+ data_str_cnt = len(str(df_string))
704
+ data = df_string
705
+
706
+ # Calculate the number of parts based on the length of the string
707
+ n_parts = data_str_cnt // word_base + (1 if data_str_cnt % word_base != 0 else 0)
708
+ print(f"Number of Parts: {n_parts}")
709
+
710
+ # Calculate the number of elements each part should have
711
+ part_size = len(data) // n_parts if n_parts > 0 else len(data)
712
+
713
+ segments = []
714
+ for i in range(n_parts):
715
+ start_idx = i * part_size
716
+ end_idx = min((i + 1) * part_size, len(data))
717
+ # Serialize the segment back to a JSON string
718
+ segment = json.dumps(data[start_idx:end_idx])
719
+ segments.append(segment)
720
+
721
+ return segments
722
+
723
+ def generate_content_by_LLM(sys_content, user_content, response_format=None):
724
+ # 使用 OpenAI 生成基于上传数据的问题
725
+
726
+ try:
727
+ model = "gpt-4-turbo"
728
+ # 使用 OPEN AI 生成 Reading Passage
729
+ messages = [
730
+ {"role": "system", "content": sys_content},
731
+ {"role": "user", "content": user_content}
732
+ ]
733
+
734
+ request_payload = {
735
+ "model": model,
736
+ "messages": messages,
737
+ "max_tokens": 4000,
738
+ "response_format": response_format
739
+ }
740
+
741
+ if response_format is not None:
742
+ request_payload["response_format"] = response_format
743
+
744
+ response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
745
+ content = response.choices[0].message.content.strip()
746
+ except Exception as e:
747
+ print(f"Error generating reading passage: {str(e)}")
748
+ print("using REDROCK")
749
+ # 使用 REDROCK 生成 Reading Passage
750
+ messages = [
751
+ {"role": "user", "content": user_content}
752
+ ]
753
+ model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
754
+ # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
755
+ kwargs = {
756
+ "modelId": model_id,
757
+ "contentType": "application/json",
758
+ "accept": "application/json",
759
+ "body": json.dumps({
760
+ "anthropic_version": "bedrock-2023-05-31",
761
+ "max_tokens": 4000,
762
+ "system": sys_content,
763
+ "messages": messages
764
+ })
765
+ }
766
+ response = BEDROCK_CLIENT.invoke_model(**kwargs)
767
+ response_body = json.loads(response.get('body').read())
768
+ content = response_body.get('content')[0].get('text')
769
+
770
+ print("=====content=====")
771
+ print(content)
772
+ print("=====content=====")
773
+
774
+ return content
775
+
776
  def get_reading_passage(video_id, df_string, source):
777
  if source == "gcs":
778
  print("===get_reading_passage on gcs===")
 
817
  return reading_passage_json
818
 
819
  def generate_reading_passage(df_string):
820
+ print("===generate_reading_passage===")
821
+ segments = split_data(df_string, word_base=100000)
822
+ all_content = []
823
+
824
+ for segment in segments:
825
+ sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
826
+ user_content = f"""
827
+ 請根據 {segment}
828
+ 文本自行判斷資料的種類
829
+ 幫我組合成 Reading Passage
830
+ 並潤稿讓文句通順
831
+ 請一定要使用繁體中文 zh-TW,並用台灣人的口語
832
+ 產生的結果不要前後文解釋,也不要敘述這篇文章怎麼產生的
833
+ 只需要專注提供 Reading Passage,字數在 500 字以內
834
+ 敘述中,請把數學或是專業術語,用 Latex 包覆($...$),並且不要去改原本的文章
835
+ 加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
836
+ 請直接給出文章,不用介紹怎麼處理的或是文章字數等等
837
+ """
838
+ content = generate_content_by_LLM(sys_content, user_content)
839
+ all_content.append(content + "\n")
840
+
841
+ # 將所有生成的閱讀理解段落合併成一個完整的文章
842
+ final_content = "\n".join(all_content)
843
+ return final_content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
844
 
845
  def text_to_speech(video_id, text):
846
  tts = gTTS(text, lang='en')
 
893
  return mind_map_json
894
 
895
  def generate_mind_map(df_string):
896
+ print("===generate_mind_map===")
897
+ segments = split_data(df_string, word_base=100000)
898
+ all_content = []
899
+
900
+ for segment in segments:
901
+ sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
902
+ user_content = f"""
903
+ 請根據 {segment} 文本建立 markdown 心智圖
904
+ 注意:不需要前後文敘述,直接給出 markdown 文本即可
905
+ 這對我很重要
906
+ """
907
+ content = generate_content_by_LLM(sys_content, user_content)
908
+ all_content.append(content + "\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
909
 
910
+ # 將所有生成的閱讀理解段落合併成一個完整的文章
911
+ final_content = "\n".join(all_content)
912
+ return final_content
913
 
914
  def get_mind_map_html(mind_map):
915
  mind_map_markdown = mind_map.replace("```markdown", "").replace("```", "")
 
978
  return summary_json
979
 
980
  def generate_summarise(df_string, metadata=None):
981
+ print("===generate_summarise===")
982
  # 使用 OpenAI 生成基于上传数据的问题
983
  if metadata:
984
  title = metadata.get("title", "")
 
989
  subject = ""
990
  grade = ""
991
 
992
+ segments = split_data(df_string, word_base=100000)
993
+ all_content = []
 
 
 
994
 
995
+ for segment in segments:
996
+ sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
997
+ user_content = f"""
998
+ 課程名稱:{title}
999
+ 科目:{subject}
1000
+ 年級:{grade}
 
 
 
1001
 
1002
+ 請根據內文: {segment}
1003
+
1004
+ 格式為 Markdown
1005
+ 如果有課程名稱,請圍繞「課程名稱」為學習重點,進行重點整理,不要整理跟情境故事相關的問題
1006
+ 整體摘要在一百字以內
1007
+ 重點概念列出 bullet points,至少三個,最多五個
1008
+ 以及可能的結論與結尾延伸小問題提供學生作反思
1009
+ 敘述中,請把數學或是專業術語,用 Latex 包覆($...$)
1010
+ 加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
1011
+
1012
+ 整體格式為:
1013
+ ## 🌟 主題:{{title}} (如果沒有 title 就省略)
1014
+ ## 📚 整體摘要
1015
+ - (一個 bullet point....)
1016
+
1017
+ ## 🔖 重點概念
1018
+ - xxx
1019
+ - xxx
1020
+ - xxx
1021
+
1022
+ ## 💡 為什麼我們要學這個?
1023
+ - (一個 bullet point....)
1024
+
1025
+ ## ❓ 延伸小問題
1026
+ - (一個 bullet point....請圍繞「課程名稱」為學習重點,進行重點整理,不要整理跟情境故事相關的問題)
1027
+ """
1028
+ content = generate_content_by_LLM(sys_content, user_content)
1029
+ all_content.append(content + "\n")
1030
+
1031
+ if len(all_content) > 1:
1032
+ all_content_cnt = len(all_content)
1033
+ all_content_str = json.dumps(all_content)
1034
+ sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生��請精讀賛料文本,自行判斷賛料的種類,使用 zh-TW"
1035
+ user_content = f"""
1036
+ 課程名稱:{title}
1037
+ 科目:{subject}
1038
+ 年級:{grade}
1039
+
1040
+ 請根據內文: {all_content_str}
1041
+ 共有 {all_content_cnt} 段,請縱整成一篇摘要
1042
+
1043
+ 格式為 Markdown
1044
+ 如果有課程名稱,請圍繞「課程名稱」為學習重點,進行重點整理,不要整理跟情境故事相關的問題
1045
+ 整體摘要在 {all_content_cnt} 百字以內
1046
+ 重點概念列出 bullet points,至少三個,最多十個
1047
+ 以及可能的結論與結尾延伸小問題提供學生作反思
1048
+ 敘述中,請把數學或是專業術語,用 Latex 包覆($...$)
1049
+ 加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
1050
+
1051
+ 整體格式為:
1052
+ ## 🌟 主題:{{title}} (如果沒有 title 就省略)
1053
+ ## 📚 整體摘要
1054
+ - ( {all_content_cnt} 個 bullet point....)
1055
+
1056
+ ## 🔖 重點概念
1057
+ - xxx
1058
+ - xxx
1059
+ - xxx
1060
+
1061
+ ## 💡 為什麼我們要學這個?
1062
+ - ( {all_content_cnt} 個 bullet point....)
1063
+
1064
+ ## ❓ 延伸小問題
1065
+ - ( {all_content_cnt} 個 bullet point....請圍繞「課程名稱」為學習重點,進行重點整理,不要整理跟情境故事相關的問題)
1066
+ """
1067
+ final_content = generate_content_by_LLM(sys_content, user_content)
1068
+ else:
1069
+ final_content = all_content[0]
1070
 
1071
+ return final_content
1072
 
1073
  def get_questions(video_id, df_string, source="gcs"):
1074
  if source == "gcs":
 
1123
  return q1, q2, q3
1124
 
1125
  def generate_questions(df_string):
1126
+ print("===generate_questions===")
1127
  # 使用 OpenAI 生成基于上传数据的问题
1128
  if isinstance(df_string, str):
1129
  df_string_json = json.loads(df_string)
 
1135
  content_text += entry["text"] + ","
1136
 
1137
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,並用既有資料為本質猜測用戶可能會問的問題,使用 zh-TW"
1138
+ user_content = f"""
1139
+ 請根據 {content_text} 生成三個問題,並用 JSON 格式返回
1140
+ 一定要使用 zh-TW,這非常重要!
1141
+
1142
+ EXAMPLE:
1143
+ {{
1144
+ questions:
1145
+ [q1的敘述text, q2的敘述text, q3的敘述text]
1146
+ }}
1147
+ """
1148
 
1149
  try:
1150
+ model = "gpt-4-turbo"
1151
  messages = [
1152
  {"role": "system", "content": sys_content},
1153
  {"role": "user", "content": user_content}
 
1160
 
1161
 
1162
  request_payload = {
1163
+ "model": model,
1164
  "messages": messages,
1165
  "max_tokens": 4000,
1166
  "response_format": response_format
 
1216
  print("questions_answers已存在于GCS中")
1217
  questions_answers_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1218
  questions_answers = json.loads(questions_answers_text)
1219
+ except Exception as e:
1220
+ print(f"Error getting questions_answers: {str(e)}")
1221
  questions = get_questions(video_id, df_string, source)
1222
  questions_answers = [{"question": q, "answer": ""} for q in questions]
1223
 
1224
  return questions_answers
1225
 
1226
  def generate_questions_answers(df_string):
1227
+ print("===generate_questions_answers===")
1228
+ segments = split_data(df_string, word_base=100000)
1229
+ all_content = []
1230
+
1231
+ for segment in segments:
1232
+ sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
1233
+ user_content = f"""
1234
+ 請根據 {segment} 生成三個問題跟答案,主要與學科有關,不要問跟情節故事相關的問題
1235
+ 答案要在最後標示出處【參考:00:01:05】,請根據時間軸 start_time 來標示
1236
+ 請確保問題跟答案都是繁體中文 zh-TW
1237
+ 答案不用是標準答案,而是帶有啟發性的蘇格拉底式問答,讓學生思考本來的問題,以及該去參考的時間點
1238
+ 並用 JSON 格式返回 list ,請一定要給三個問題跟答案,且要裝在一個 list 裡面
1239
+ k-v pair 的 key 是 question, value 是 answer
1240
+
1241
+ EXAMPLE:
1242
+ {{
1243
+ "questions_answers":
1244
+ [
1245
+ {{question: q1的敘述text, answer: q1的答案text【參考:00:01:05】}},
1246
+ {{question: q2的敘述text, answer: q2的答案text【參考:00:32:05】}},
1247
+ {{question: q3的敘述text, answer: q3的答案text【參考:01:03:35】}}
1248
+ ]
1249
+ }}
1250
+ """
1251
  response_format = { "type": "json_object" }
1252
+ content = generate_content_by_LLM(sys_content, user_content, response_format)
1253
+ content_json = json.loads(content)["questions_answers"]
1254
+ all_content += content_json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1255
 
1256
+ print("=====all_content=====")
1257
+ print(all_content)
1258
+ print("=====all_content=====")
 
 
1259
 
1260
+ return all_content
1261
 
1262
  def change_questions(password, df_string):
1263
  verify_password(password)
 
1334
  return key_moments_json
1335
 
1336
  def generate_key_moments(formatted_simple_transcript, formatted_transcript):
1337
+ print("===generate_key_moments===")
1338
  # 使用 OpenAI 生成基于上传数据的问题
1339
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
1340
  user_content = f"""
 
1345
  4. 如果頭尾的情節不是重點,特別是打招呼或是介紹人物、或是say goodbye 就是不重要的情節,就不用擷取
1346
  5. 以這種方式分析整個文本,從零秒開始分析,直到結束。這很重要
1347
  6. 關鍵字從transcript extract to keyword,保留專家名字、專業術語、年份、數字、期刊名稱、地名、數學公式
1348
+ 7. text, keywords please use or transfer to zh-TW, it's very important
1349
 
1350
  Example: retrun JSON
1351
  {{key_moments:[{{
 
1357
  }}
1358
  """
1359
 
1360
+ segments = split_data(formatted_simple_transcript, word_base=100000)
1361
+ all_content = []
1362
+
1363
+ for segment in segments:
1364
+ sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
1365
+ user_content = f"""
1366
+ 請根據 {segment} 文本,提取出重點摘要,並給出對應的時間軸
1367
+ 1. 小範圍切出不同段落的相對應時間軸的重點摘要,
1368
+ 2. 每一小段最多不超過 1/5 的總內容,也就是大約 3~5段的重點(例如五~十分鐘的影片就一段大約1~2分鐘,最多三分鐘,但如果是超過十分鐘的影片,那一小段大約 2~3分鐘,以此類推)
1369
+ 3. 注意不要遺漏任何一段時間軸的內容 從零秒開始
1370
+ 4. 如果頭尾的情節不是重點,特別是打招呼或是介紹人物、或是say goodbye 就是不重要的情節,就不用擷取
1371
+ 5. 以這種方式分析整個文本,從零秒開始分析,直到結束。這很重要
1372
+ 6. 關鍵字從transcript extract to keyword,保留專家名字、專業術語、年份、數字、期刊名稱、地名、數學公式
1373
+ 7. text, keywords please use or transfer zh-TW, it's very important
1374
+
1375
+ Example: retrun JSON
1376
+ {{key_moments:[{{
1377
+ "start": "00:00",
1378
+ "end": "01:00",
1379
+ "text": "逐字稿的重點摘要",
1380
+ "keywords": ["關鍵字", "關鍵字"]
1381
+ }}]
1382
+ }}
1383
+ """
1384
  response_format = { "type": "json_object" }
1385
+ content = generate_content_by_LLM(sys_content, user_content, response_format)
1386
+ key_moments = json.loads(content)["key_moments"]
1387
+
1388
+ # "transcript": get text from formatted_simple_transcript
1389
+ for moment in key_moments:
1390
+ start_time = parse_time(moment['start'])
1391
+ end_time = parse_time(moment['end'])
1392
+ # 使用轉換後的 timedelta 物件進行時間
1393
+ moment['transcript'] = ",".join([entry['text'] for entry in formatted_simple_transcript
1394
+ if start_time <= parse_time(entry['start_time']) <= end_time])
1395
+
1396
+ print("=====key_moments=====")
1397
+ print(key_moments)
1398
+ print("=====key_moments=====")
1399
+ image_links = {entry['start_time']: entry['screenshot_path'] for entry in formatted_transcript}
1400
+
1401
+ for moment in key_moments:
1402
+ start_time = parse_time(moment['start'])
1403
+ end_time = parse_time(moment['end'])
1404
+ # 使用轉換後的 timedelta 物件進行時間比較
1405
+ moment_images = [image_links[time] for time in image_links
1406
+ if start_time <= parse_time(time) <= end_time]
1407
+ moment['images'] = moment_images
1408
+
1409
+ all_content += key_moments
1410
+
1411
+ return all_content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1412
 
1413
  def generate_key_moments_keywords(transcript):
1414
+ print("===generate_key_moments_keywords===")
1415
+ segments = split_data(transcript, word_base=100000)
1416
+ all_content = []
1417
+
1418
+ for segment in segments:
1419
+ sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
1420
+ user_content = f"""
1421
+ transcript extract to keyword
1422
+ 保留專家名字、專業術語、年份、數字、期刊名稱、地名、數學公式、數學表示式、物理化學符號,
1423
+ 不用給上下文,直接給出關鍵字,使用 zh-TW,用逗號分隔, example: 關鍵字1, 關鍵字2
1424
+ transcript:{segment}
1425
+ """
1426
+ content = generate_content_by_LLM(sys_content, user_content)
1427
+ keywords = content.strip().split(",")
1428
+ all_content += keywords
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1429
 
1430
+ return all_content
1431
 
1432
  def get_key_moments_html(key_moments):
1433
  css = """
 
2774
  with gr.Column(scale=1, variant="panel"):
2775
  foxcat_chatbot_avatar_url = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2020/06/%E7%A7%91%E5%AD%B8%E5%BE%BD%E7%AB%A0-2-150x150.png"
2776
  foxcat_avatar_images = gr.State([user_avatar, foxcat_chatbot_avatar_url])
2777
+ foxcat_chatbot_description = """Hi,我是【狐狸貓】,可以陪你一起學習本次的內容,有什麼問題都可以問我喔!\n
2778
+ 🤔 三年級學生|10 歲|男\n
2779
+ 🗣️ 口頭禪:「感覺好好玩喔!」「咦?是這樣嗎?」\n
2780
+ 🔠 興趣:看知識型書籍、熱血的動漫卡通、料理、爬山、騎腳踏車。因為太喜歡吃魚了,正努力和爸爸學習釣魚、料理魚及各種有關魚的知識,最討厭的食物是青椒。\n
2781
+ 💤 個性:喜歡學習新知,擁有最旺盛的好奇心,家裡堆滿百科全書,例如:國家地理頻道出版的「終極魚百科」,雖都沒有看完,常常被梨梨唸是三分鐘熱度,但是也一點一點學習到不同領域的知識。雖然有時會忘東忘��,但認真起來也是很可靠,答應的事絕對使命必達。遇到挑戰時,勇於跳出舒適圈,追求自我改變,視困難為成長的機會。
 
2782
  """
2783
  foxcat_chatbot_name = gr.State("foxcat")
2784
  gr.Image(value=foxcat_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
 
2789
  with gr.Column(scale=1, variant="panel"):
2790
  lili_chatbot_avatar_url = "https://junyitopicimg.s3.amazonaws.com/live/v1283-new-topic-44-icon.png?v=20230529071206714"
2791
  lili_avatar_images = gr.State([user_avatar, lili_chatbot_avatar_url])
2792
+ lili_chatbot_description = """你好,我是溫柔的【梨梨】,很高興可以在這裡陪伴你學習。如果你有任何疑問,請隨時向我提出哦! \n
2793
+ 🤔 三年級學生|10 歲|女\n
2794
+ 🗣️ 口頭禪:「真的假的?!」「讓我想一想喔」「你看吧!大問題拆解成小問題,就變得簡單啦!」「混混噩噩的生活不值得過」\n
2795
+ 🔠 興趣:烘焙餅乾(父母開糕餅店)、畫畫、聽流行音樂、收納。\n
2796
+ 💤 個性:
2797
+ - 內向害羞,比起出去玩更喜歡待在家(除非是跟狐狸貓出去玩)
2798
+ - 數理邏輯很好;其實覺得麥麥連珠炮的提問有點煩,但還是會耐心地回答
2799
+ - 有驚人的眼力,總能觀察到其他人沒有察覺的細節
2800
+ - 喜歡整整齊齊的環境,所以一到麥麥家就受不了
2801
  """
2802
  lili_chatbot_name = gr.State("lili")
2803
  gr.Image(value=lili_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
 
2808
  with gr.Column(scale=1, variant="panel"):
2809
  maimai_chatbot_avatar_url = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2020/07/%E6%80%9D%E8%80%83%E5%8A%9B%E8%B6%85%E4%BA%BA%E5%BE%BD%E7%AB%A0_%E5%B7%A5%E4%BD%9C%E5%8D%80%E5%9F%9F-1-%E8%A4%87%E6%9C%AC-150x150.png"
2810
  maimai_avatar_images = gr.State([user_avatar, maimai_chatbot_avatar_url])
2811
+ maimai_chatbot_description = """Hi,我是迷人的【麥麥】,我在這裡等著和你一起探索新知,任何疑問都可以向我提出!\n
2812
+ 🤔 三年級學生|10 歲|男\n
2813
+ 🗣️ 口頭禪:「Oh My God!」「好奇怪喔!」「喔!原來是這樣啊!」\n
2814
+ 🔠 興趣:最愛去野外玩耍(心情好時會順便捕魚送給狐狸貓),喜歡講冷笑話、惡作劇。因為太喜歡玩具,而開始自己做玩具,家裡就好像他的遊樂場。\n
2815
+ 💤 個性:喜歡問問題,就算被梨梨ㄘㄟ,也還是照問|憨厚,外向好動,樂天開朗,不會被難題打敗|喜歡收集各式各樣的東西;房間只有在整理��那一天最乾淨
 
2816
  """
2817
  maimai_chatbot_name = gr.State("maimai")
2818
  gr.Image(value=maimai_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)