youngtsai commited on
Commit
10f0966
1 Parent(s): 6835560
Files changed (4) hide show
  1. app.py +765 -546
  2. chatbot.py +22 -8
  3. educational_material.py +110 -62
  4. storage_service.py +11 -0
app.py CHANGED
@@ -92,96 +92,6 @@ def verify_password(password):
92
  else:
93
  raise gr.Error("密碼錯誤")
94
 
95
- # ====gcs====
96
- def gcs_check_file_exists(gcs_client, bucket_name, file_name):
97
- """
98
- 检查 GCS 存储桶中是否存在指定的文件
99
- file_name 格式:{folder_name}/{file_name}
100
- """
101
- bucket = gcs_client.bucket(bucket_name)
102
- blob = bucket.blob(file_name)
103
- return blob.exists()
104
-
105
- def upload_file_to_gcs(gcs_client, bucket_name, destination_blob_name, file_path):
106
- """上传文件到指定的 GCS 存储桶"""
107
- bucket = gcs_client.bucket(bucket_name)
108
- blob = bucket.blob(destination_blob_name)
109
- blob.upload_from_filename(file_path)
110
- print(f"File {file_path} uploaded to {destination_blob_name} in GCS.")
111
-
112
- def upload_file_to_gcs_with_json_string(gcs_client, bucket_name, destination_blob_name, json_string):
113
- """上传字符串到指定的 GCS 存储桶"""
114
- bucket = gcs_client.bucket(bucket_name)
115
- blob = bucket.blob(destination_blob_name)
116
- blob.upload_from_string(json_string)
117
- print(f"JSON string uploaded to {destination_blob_name} in GCS.")
118
-
119
- def download_blob_to_string(gcs_client, bucket_name, source_blob_name):
120
- """从 GCS 下载文件内容到字符串"""
121
- bucket = gcs_client.bucket(bucket_name)
122
- blob = bucket.blob(source_blob_name)
123
- return blob.download_as_text()
124
-
125
- def make_blob_public(gcs_client, bucket_name, blob_name):
126
- """将指定的 GCS 对象设置为公共可读"""
127
- bucket = gcs_client.bucket(bucket_name)
128
- blob = bucket.blob(blob_name)
129
- blob.make_public()
130
- print(f"Blob {blob_name} is now publicly accessible at {blob.public_url}")
131
-
132
- def get_blob_public_url(gcs_client, bucket_name, blob_name):
133
- """获取指定 GCS 对象的公开 URL"""
134
- bucket = gcs_client.bucket(bucket_name)
135
- blob = bucket.blob(blob_name)
136
- return blob.public_url
137
-
138
- def upload_img_and_get_public_url(gcs_client, bucket_name, file_name, file_path):
139
- """上传图片到 GCS 并获取其公开 URL"""
140
- # 上传图片
141
- upload_file_to_gcs(gcs_client, bucket_name, file_name, file_path)
142
- # 将上传的图片设置为公开
143
- make_blob_public(gcs_client, bucket_name, file_name)
144
- # 获取图片的公开 URL
145
- public_url = get_blob_public_url(gcs_client, bucket_name, file_name)
146
- print(f"Public URL for the uploaded image: {public_url}")
147
- return public_url
148
-
149
- def copy_all_files_from_drive_to_gcs(drive_service, gcs_client, drive_folder_id, bucket_name, gcs_folder_name):
150
- # Get all files from the folder
151
- query = f"'{drive_folder_id}' in parents and trashed = false"
152
- response = drive_service.files().list(q=query).execute()
153
- files = response.get('files', [])
154
- for file in files:
155
- # Copy each file to GCS
156
- file_id = file['id']
157
- file_name = file['name']
158
- gcs_destination_path = f"{gcs_folder_name}/{file_name}"
159
- copy_file_from_drive_to_gcs(drive_service, gcs_client, file_id, bucket_name, gcs_destination_path)
160
-
161
- def copy_file_from_drive_to_gcs(drive_service, gcs_client, file_id, bucket_name, gcs_destination_path):
162
- # Download file content from Drive
163
- request = drive_service.files().get_media(fileId=file_id)
164
- fh = io.BytesIO()
165
- downloader = MediaIoBaseDownload(fh, request)
166
- done = False
167
- while not done:
168
- status, done = downloader.next_chunk()
169
- fh.seek(0)
170
- file_content = fh.getvalue()
171
-
172
- # Upload file content to GCS
173
- bucket = gcs_client.bucket(bucket_name)
174
- blob = bucket.blob(gcs_destination_path)
175
- blob.upload_from_string(file_content)
176
- print(f"File {file_id} copied to GCS at {gcs_destination_path}.")
177
-
178
- def delete_blob(gcs_client, bucket_name, blob_name):
179
- """删除指定的 GCS 对象"""
180
- bucket = gcs_client.bucket(bucket_name)
181
- blob = bucket.blob(blob_name)
182
- blob.delete()
183
- print(f"Blob {blob_name} deleted from GCS.")
184
-
185
  # # ====drive====初始化
186
  def init_drive_service():
187
  credentials_json_string = DRIVE_KEY
@@ -381,7 +291,11 @@ def extract_youtube_id(url):
381
  return None
382
 
383
  def get_transcript_by_yt_api(video_id):
384
- languages = ['zh-TW', 'zh-Hant', 'zh', 'en-US'] # 優先順序列表
 
 
 
 
385
  for language in languages:
386
  try:
387
  transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=[language])
@@ -483,12 +397,13 @@ def process_transcript_and_screenshots_on_gcs(video_id):
483
  transcript = generate_transcription_by_whisper(video_id)
484
 
485
  transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
486
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, transcript_blob_name, transcript_text)
 
487
  is_new_transcript = True
488
  else:
489
  # 逐字稿已存在,下载逐字稿内容
490
  print("逐字稿已存在于GCS中")
491
- transcript_text = download_blob_to_string(gcs_client, bucket_name, transcript_blob_name)
492
  transcript = json.loads(transcript_text)
493
 
494
  # print("===確認其他衍生文件===")
@@ -517,7 +432,7 @@ def process_transcript_and_screenshots_on_gcs(video_id):
517
  # 截图
518
  screenshot_path = screenshot_youtube_video(video_id, entry['start'])
519
  screenshot_blob_name = f"{video_id}/{video_id}_{entry['start']}.jpg"
520
- img_file_id = upload_img_and_get_public_url(gcs_client, bucket_name, screenshot_blob_name, screenshot_path)
521
  entry['img_file_id'] = img_file_id
522
  print(f"截图已上传到GCS: {img_file_id}")
523
  is_new_transcript = True
@@ -529,7 +444,7 @@ def process_transcript_and_screenshots_on_gcs(video_id):
529
  print(transcript)
530
  print("===更新逐字稿文件===")
531
  updated_transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
532
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, transcript_blob_name, updated_transcript_text)
533
  print("逐字稿已更新,包括截图链接")
534
  updated_transcript_json = json.loads(updated_transcript_text)
535
  else:
@@ -723,12 +638,12 @@ def get_reading_passage(video_id, df_string, source):
723
  reading_passage = generate_reading_passage(df_string)
724
  reading_passage_json = {"reading_passage": str(reading_passage)}
725
  reading_passage_text = json.dumps(reading_passage_json, ensure_ascii=False, indent=2)
726
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, reading_passage_text)
727
  print("reading_passage已上传到GCS")
728
  else:
729
  # reading_passage已存在,下载内容
730
  print("reading_passage已存在于GCS中")
731
- reading_passage_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
732
  reading_passage_json = json.loads(reading_passage_text)
733
 
734
  elif source == "drive":
@@ -767,19 +682,44 @@ def generate_reading_passage(df_string):
767
  敘述中,請把數學或是專業術語,用 Latex 包覆($...$),並且不要去改原本的文章
768
  加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
769
  """
770
- messages = [
771
- {"role": "system", "content": sys_content},
772
- {"role": "user", "content": user_content}
773
- ]
774
 
775
- request_payload = {
776
- "model": "gpt-4-turbo",
777
- "messages": messages,
778
- "max_tokens": 4000,
779
- }
 
780
 
781
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
782
- reading_passage = response.choices[0].message.content.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
783
  print("=====reading_passage=====")
784
  print(reading_passage)
785
  print("=====reading_passage=====")
@@ -805,12 +745,12 @@ def get_mind_map(video_id, df_string, source):
805
  mind_map = generate_mind_map(df_string)
806
  mind_map_json = {"mind_map": str(mind_map)}
807
  mind_map_text = json.dumps(mind_map_json, ensure_ascii=False, indent=2)
808
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, mind_map_text)
809
  print("mind_map已上傳到GCS")
810
  else:
811
  # mindmap已存在,下载内容
812
  print("mind_map已存在于GCS中")
813
- mind_map_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
814
  mind_map_json = json.loads(mind_map_text)
815
 
816
  elif source == "drive":
@@ -844,19 +784,43 @@ def generate_mind_map(df_string):
844
  注意:不需要前後文敘述,直接給出 markdown 文本即可
845
  這對我很重要
846
  """
847
- messages = [
848
- {"role": "system", "content": sys_content},
849
- {"role": "user", "content": user_content}
850
- ]
851
 
852
- request_payload = {
853
- "model": "gpt-4-turbo",
854
- "messages": messages,
855
- "max_tokens": 4000,
856
- }
 
857
 
858
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
859
- mind_map = response.choices[0].message.content.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
860
  print("=====mind_map=====")
861
  print(mind_map)
862
  print("=====mind_map=====")
@@ -889,12 +853,12 @@ def get_video_id_summary(video_id, df_string, source):
889
  summary = generate_summarise(df_string, meta_data)
890
  summary_json = {"summary": str(summary)}
891
  summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
892
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, summary_file_blob_name, summary_text)
893
  print("summary已上传到GCS")
894
  else:
895
  # summary已存在,下载内容
896
  print("summary已存在于GCS中")
897
- summary_text = download_blob_to_string(gcs_client, bucket_name, summary_file_blob_name)
898
  summary_json = json.loads(summary_text)
899
 
900
  elif source == "drive":
@@ -980,19 +944,44 @@ def generate_summarise(df_string, metadata=None):
980
  # 💡 5. 結論反思(為什麼我們要學這個?)
981
  # ❓ 6. 延伸小問題
982
 
983
- messages = [
984
- {"role": "system", "content": sys_content},
985
- {"role": "user", "content": user_content}
986
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
987
 
988
- request_payload = {
989
- "model": "gpt-4-turbo",
990
- "messages": messages,
991
- "max_tokens": 4000,
992
- }
993
 
994
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
995
- df_summarise = response.choices[0].message.content.strip()
996
  print("=====df_summarise=====")
997
  print(df_summarise)
998
  print("=====df_summarise=====")
@@ -1012,12 +1001,12 @@ def get_questions(video_id, df_string, source="gcs"):
1012
  if not is_questions_exists:
1013
  questions = generate_questions(df_string)
1014
  questions_text = json.dumps(questions, ensure_ascii=False, indent=2)
1015
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, questions_text)
1016
  print("questions已上傳到GCS")
1017
  else:
1018
  # 逐字稿已存在,下载逐字稿内容
1019
  print("questions已存在于GCS中")
1020
- questions_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
1021
  questions = json.loads(questions_text)
1022
 
1023
  elif source == "drive":
@@ -1064,26 +1053,50 @@ def generate_questions(df_string):
1064
 
1065
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,並用既有資料為本質猜測用戶可能會問的問題,使用 zh-TW"
1066
  user_content = f"請根據 {content_text} 生成三個問題,並用 JSON 格式返回 questions:[q1的敘述text, q2的敘述text, q3的敘述text]"
1067
- messages = [
1068
- {"role": "system", "content": sys_content},
1069
- {"role": "user", "content": user_content}
1070
- ]
1071
- response_format = { "type": "json_object" }
 
 
1072
 
1073
- print("=====messages=====")
1074
- print(messages)
1075
- print("=====messages=====")
1076
 
1077
 
1078
- request_payload = {
1079
- "model": "gpt-4-turbo",
1080
- "messages": messages,
1081
- "max_tokens": 4000,
1082
- "response_format": response_format
1083
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1084
 
1085
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1086
- questions = json.loads(response.choices[0].message.content)["questions"]
1087
  print("=====json_response=====")
1088
  print(questions)
1089
  print("=====json_response=====")
@@ -1103,12 +1116,12 @@ def get_questions_answers(video_id, df_string, source="gcs"):
1103
  if not is_questions_answers_exists:
1104
  questions_answers = generate_questions_answers(df_string)
1105
  questions_answers_text = json.dumps(questions_answers, ensure_ascii=False, indent=2)
1106
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, questions_answers_text)
1107
  print("questions_answers已上傳到GCS")
1108
  else:
1109
  # questions_answers已存在,下载内容
1110
  print("questions_answers已存在于GCS中")
1111
- questions_answers_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
1112
  questions_answers = json.loads(questions_answers_text)
1113
  except:
1114
  questions = get_questions(video_id, df_string, source)
@@ -1202,12 +1215,12 @@ def get_key_moments(video_id, formatted_simple_transcript, formatted_transcript,
1202
  key_moments = generate_key_moments(formatted_simple_transcript, formatted_transcript)
1203
  key_moments_json = {"key_moments": key_moments}
1204
  key_moments_text = json.dumps(key_moments_json, ensure_ascii=False, indent=2)
1205
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, key_moments_text)
1206
  print("key_moments已上傳到GCS")
1207
  else:
1208
  # key_moments已存在,下载内容
1209
  print("key_moments已存在于GCS中")
1210
- key_moments_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
1211
  key_moments_json = json.loads(key_moments_text)
1212
  # 檢查 key_moments 是否有 keywords
1213
  print("===檢查 key_moments 是否有 keywords===")
@@ -1222,8 +1235,8 @@ def get_key_moments(video_id, formatted_simple_transcript, formatted_transcript,
1222
  has_keywords_added = True
1223
  if has_keywords_added:
1224
  key_moments_text = json.dumps(key_moments_json, ensure_ascii=False, indent=2)
1225
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, key_moments_text)
1226
- key_moments_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
1227
  key_moments_json = json.loads(key_moments_text)
1228
 
1229
  elif source == "drive":
@@ -1271,30 +1284,53 @@ def generate_key_moments(formatted_simple_transcript, formatted_transcript):
1271
  "keywords": ["關鍵字", "關鍵字"]
1272
  }}]
1273
  """
1274
- messages = [
1275
- {"role": "system", "content": sys_content},
1276
- {"role": "user", "content": user_content}
1277
- ]
1278
- response_format = { "type": "json_object" }
1279
-
1280
- request_payload = {
1281
- "model": "gpt-4-turbo",
1282
- "messages": messages,
1283
- "max_tokens": 4096,
1284
- "response_format": response_format
1285
- }
1286
 
1287
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1288
  response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1289
  print("===response===")
1290
  print(dict(response))
1291
  key_moments = json.loads(response.choices[0].message.content)["key_moments"]
1292
  except Exception as e:
1293
- error_msg = f" {video_id} 關鍵時刻錯誤: {str(e)}"
1294
  print("===generate_key_moments error===")
1295
  print(error_msg)
1296
  print("===generate_key_moments error===")
1297
- raise Exception(error_msg)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1298
 
1299
  print("=====key_moments=====")
1300
  print(key_moments)
@@ -1318,18 +1354,43 @@ def generate_key_moments_keywords(transcript):
1318
  不用給上下文,直接給出關鍵字,使用 zh-TW,用逗號分隔, example: 關鍵字1, 關鍵字2
1319
  transcript:{transcript}
1320
  """
1321
- messages = [
1322
- {"role": "system", "content": system_content},
1323
- {"role": "user", "content": user_content}
1324
- ]
1325
- request_payload = {
1326
- "model": "gpt-4-turbo",
1327
- "messages": messages,
1328
- "max_tokens": 100,
1329
- }
1330
 
1331
- response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1332
- keywords = response.choices[0].message.content.strip().split(", ")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1333
 
1334
  return keywords
1335
 
@@ -1415,7 +1476,7 @@ def get_key_moments_html(key_moments):
1415
  background-color: black;
1416
  border-radius: 50%;
1417
  text-decoration: none;
1418
- color: black;
1419
  opacity: 0.8;
1420
  transition: opacity 200ms ease;
1421
  }
@@ -1503,10 +1564,10 @@ def get_key_moments_html(key_moments):
1503
  image_elements += f"""
1504
  <div id="{current_id}" class="gallery__item">
1505
  <a href="#{prev_id}" class="click-zone click-zone-prev">
1506
- <div class="arrow arrow-disabled arrow-prev"> < </div>
1507
  </a>
1508
  <a href="#{next_id}" class="click-zone click-zone-next">
1509
- <div class="arrow arrow-next"> > </div>
1510
  </a>
1511
  <img src="{image}">
1512
  </div>
@@ -1545,7 +1606,7 @@ def get_LLM_content(video_id, kind):
1545
  # 检查 file 是否存在
1546
  is_file_exists = GCS_SERVICE.check_file_exists(bucket_name, blob_name)
1547
  if is_file_exists:
1548
- content = download_blob_to_string(gcs_client, bucket_name, blob_name)
1549
  content_json = json.loads(content)
1550
  if kind == "reading_passage_latex":
1551
  content_text = content_json["reading_passage"]
@@ -1569,7 +1630,7 @@ def delete_LLM_content(video_id, kind):
1569
  # 检查 file 是否存在
1570
  is_file_exists = GCS_SERVICE.check_file_exists(bucket_name, blob_name)
1571
  if is_file_exists:
1572
- delete_blob(gcs_client, bucket_name, blob_name)
1573
  print(f"{file_name}已从GCS中删除")
1574
  return gr.update(value="", interactive=False)
1575
 
@@ -1585,17 +1646,17 @@ def update_LLM_content(video_id, new_content, kind):
1585
  print(new_content)
1586
  reading_passage_json = {"reading_passage": str(new_content)}
1587
  reading_passage_text = json.dumps(reading_passage_json, ensure_ascii=False, indent=2)
1588
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, reading_passage_text)
1589
  updated_content = new_content
1590
  elif kind == "summary_markdown":
1591
  summary_json = {"summary": str(new_content)}
1592
  summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
1593
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, summary_text)
1594
  updated_content = new_content
1595
  elif kind == "mind_map":
1596
  mind_map_json = {"mind_map": str(new_content)}
1597
  mind_map_text = json.dumps(mind_map_json, ensure_ascii=False, indent=2)
1598
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, mind_map_text)
1599
  updated_content = mind_map_text
1600
  elif kind == "key_moments":
1601
  # from update_LLM_btn -> new_content is a string
@@ -1606,7 +1667,7 @@ def update_LLM_content(video_id, new_content, kind):
1606
  key_moments_list = new_content
1607
  key_moments_json = {"key_moments": key_moments_list}
1608
  key_moments_text = json.dumps(key_moments_json, ensure_ascii=False, indent=2)
1609
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, key_moments_text)
1610
  updated_content = key_moments_text
1611
  elif kind == "transcript":
1612
  if isinstance(new_content, str):
@@ -1614,7 +1675,7 @@ def update_LLM_content(video_id, new_content, kind):
1614
  else:
1615
  transcript_json = new_content
1616
  transcript_text = json.dumps(transcript_json, ensure_ascii=False, indent=2)
1617
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, transcript_text)
1618
  updated_content = transcript_text
1619
  elif kind == "questions":
1620
  # from update_LLM_btn -> new_content is a string
@@ -1624,7 +1685,7 @@ def update_LLM_content(video_id, new_content, kind):
1624
  else:
1625
  questions_json = new_content
1626
  questions_text = json.dumps(questions_json, ensure_ascii=False, indent=2)
1627
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, questions_text)
1628
  updated_content = questions_text
1629
  elif kind == "questions_answers":
1630
  # from update_LLM_btn -> new_content is a string
@@ -1634,7 +1695,7 @@ def update_LLM_content(video_id, new_content, kind):
1634
  else:
1635
  questions_answers_json = new_content
1636
  questions_answers_text = json.dumps(questions_answers_json, ensure_ascii=False, indent=2)
1637
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, questions_answers_text)
1638
  updated_content = questions_answers_text
1639
 
1640
  print(f"{kind} 已更新到GCS")
@@ -1688,7 +1749,6 @@ def create_LLM_content(video_id, df_string, kind):
1688
  def reading_passage_add_latex_version(video_id):
1689
  # 確認 GCS 是否有 reading_passage.json
1690
  print("===reading_passage_convert_to_latex===")
1691
- gcs_client = GCS_CLIENT
1692
  bucket_name = 'video_ai_assistant'
1693
  file_name = f'{video_id}_reading_passage.json'
1694
  blob_name = f"{video_id}/{file_name}"
@@ -1701,7 +1761,7 @@ def reading_passage_add_latex_version(video_id):
1701
 
1702
  # 逐字稿已存在,下载逐字稿内容
1703
  print("reading_passage 已存在于GCS中,轉換 Latex 模式")
1704
- reading_passage_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
1705
  reading_passage_json = json.loads(reading_passage_text)
1706
  original_reading_passage = reading_passage_json["reading_passage"]
1707
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
@@ -1734,14 +1794,13 @@ def reading_passage_add_latex_version(video_id):
1734
  # 另存為 reading_passage_latex.json
1735
  new_file_name = f'{video_id}_reading_passage_latex.json'
1736
  new_blob_name = f"{video_id}/{new_file_name}"
1737
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, new_blob_name, reading_passage_text)
1738
 
1739
  return new_reading_passage
1740
 
1741
  def summary_add_markdown_version(video_id):
1742
  # 確認 GCS 是否有 summary.json
1743
  print("===summary_convert_to_markdown===")
1744
- gcs_client = GCS_CLIENT
1745
  bucket_name = 'video_ai_assistant'
1746
  file_name = f'{video_id}_summary.json'
1747
  blob_name = f"{video_id}/{file_name}"
@@ -1754,7 +1813,7 @@ def summary_add_markdown_version(video_id):
1754
 
1755
  # 逐字稿已存在,下载逐字稿内容
1756
  print("summary 已存在于GCS中,轉換 Markdown 模式")
1757
- summary_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
1758
  summary_json = json.loads(summary_text)
1759
  original_summary = summary_json["summary"]
1760
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
@@ -1803,7 +1862,7 @@ def summary_add_markdown_version(video_id):
1803
  # 另存為 summary_markdown.json
1804
  new_file_name = f'{video_id}_summary_markdown.json'
1805
  new_blob_name = f"{video_id}/{new_file_name}"
1806
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, new_blob_name, summary_text)
1807
 
1808
  return new_summary
1809
 
@@ -1827,7 +1886,7 @@ def get_meta_data(video_id, source="gcs"):
1827
  else:
1828
  # meta_data已存在,下载内容
1829
  print("meta_data已存在于GCS中")
1830
- meta_data_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
1831
  meta_data_json = json.loads(meta_data_text)
1832
 
1833
  # meta_data_json grade 數字轉換成文字
@@ -1855,7 +1914,6 @@ def get_ai_content(password, video_id, df_string, topic, grade, level, specific_
1855
  verify_password(password)
1856
  if source == "gcs":
1857
  print("===get_ai_content on gcs===")
1858
- gcs_client = GCS_CLIENT
1859
  bucket_name = 'video_ai_assistant'
1860
  file_name = f'{video_id}_ai_content_list.json'
1861
  blob_name = f"{video_id}/{file_name}"
@@ -1865,11 +1923,11 @@ def get_ai_content(password, video_id, df_string, topic, grade, level, specific_
1865
  # 先建立一個 ai_content_list.json
1866
  ai_content_list = []
1867
  ai_content_text = json.dumps(ai_content_list, ensure_ascii=False, indent=2)
1868
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, ai_content_text)
1869
  print("ai_content_list [] 已上傳到GCS")
1870
 
1871
  # 此時 ai_content_list 已存在
1872
- ai_content_list_string = download_blob_to_string(gcs_client, bucket_name, blob_name)
1873
  ai_content_list = json.loads(ai_content_list_string)
1874
  # by key 找到 ai_content (topic, grade, level, specific_feature, content_type)
1875
  target_kvs = {
@@ -1896,7 +1954,7 @@ def get_ai_content(password, video_id, df_string, topic, grade, level, specific_
1896
 
1897
  ai_content_list.append(ai_content_json)
1898
  ai_content_text = json.dumps(ai_content_list, ensure_ascii=False, indent=2)
1899
- upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, ai_content_text)
1900
  print("ai_content已上傳到GCS")
1901
  else:
1902
  ai_content_json = ai_content_json[-1]
@@ -1909,30 +1967,26 @@ def generate_ai_content(password, df_string, topic, grade, level, specific_featu
1909
  verify_password(password)
1910
  material = EducationalMaterial(df_string, topic, grade, level, specific_feature, content_type)
1911
  prompt = material.generate_content_prompt()
1912
- user_content = material.build_user_content()
1913
- messages = material.build_messages(user_content)
1914
- ai_model_name = "gpt-4-turbo"
1915
- request_payload = {
1916
- "model": ai_model_name,
1917
- "messages": messages,
1918
- "max_tokens": 4000 # 举例,实际上您可能需要更详细的配置
1919
- }
1920
- ai_content = material.send_ai_request(OPEN_AI_CLIENT, request_payload)
1921
  return ai_content, prompt
1922
 
1923
  def generate_exam_fine_tune_result(password, exam_result_prompt , df_string_output, exam_result, exam_result_fine_tune_prompt):
1924
  verify_password(password)
1925
  material = EducationalMaterial(df_string_output, "", "", "", "", "")
1926
- user_content = material.build_fine_tune_user_content(exam_result_prompt, exam_result, exam_result_fine_tune_prompt)
1927
- messages = material.build_messages(user_content)
1928
- ai_model_name = "gpt-4-turbo"
1929
- request_payload = {
1930
- "model": ai_model_name,
1931
- "messages": messages,
1932
- "max_tokens": 4000 # 举例,实际上您可能需要更详细的配置
1933
- }
1934
- ai_content = material.send_ai_request(OPEN_AI_CLIENT, request_payload)
1935
- return ai_content
1936
 
1937
  def return_original_exam_result(exam_result_original):
1938
  return exam_result_original
@@ -2001,17 +2055,42 @@ def chat_with_ai(ai_name, password, video_id, user_data, trascript_state, key_mo
2001
  error_msg = "此次對話超過上限(對話一輪10次)"
2002
  raise gr.Error(error_msg)
2003
 
2004
- if not ai_name in ["jutor", "claude3", "groq"]:
2005
- ai_name = "jutor"
2006
-
2007
- if ai_name == "jutor":
2008
- ai_client = ""
2009
- elif ai_name == "claude3":
2010
- ai_client = BEDROCK_CLIENT
2011
- elif ai_name == "groq":
2012
- ai_client = GROQ_CLIENT
2013
- else:
2014
- ai_client = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2015
 
2016
  if isinstance(trascript_state, str):
2017
  simple_transcript = json.loads(trascript_state)
@@ -2038,14 +2117,14 @@ def chat_with_ai(ai_name, password, video_id, user_data, trascript_state, key_mo
2038
  "content_subject": content_subject,
2039
  "content_grade": content_grade,
2040
  "jutor_chat_key": JUTOR_CHAT_KEY,
2041
- "ai_name": ai_name,
2042
  "ai_client": ai_client,
2043
  "instructions": instructions
2044
  }
2045
 
2046
  try:
2047
  chatbot = Chatbot(chatbot_config)
2048
- response_completion = chatbot.chat(user_message, chat_history, socratic_mode, ai_name)
2049
  except Exception as e:
2050
  print(f"Error: {e}")
2051
  response_completion = "學習精靈有點累,請稍後再試!"
@@ -2359,19 +2438,35 @@ def create_thread_id():
2359
  return thread_id
2360
 
2361
  def chatbot_select(chatbot_name):
2362
- chatbot_select_accordion_visible = gr.update(open=False)
 
2363
  chatbot_open_ai_visible = gr.update(visible=False)
2364
  chatbot_open_ai_streaming_visible = gr.update(visible=False)
2365
  chatbot_jutor_visible = gr.update(visible=False)
 
2366
 
2367
  if chatbot_name == "chatbot_open_ai":
2368
  chatbot_open_ai_visible = gr.update(visible=True)
2369
  elif chatbot_name == "chatbot_open_ai_streaming":
2370
  chatbot_open_ai_streaming_visible = gr.update(visible=True)
2371
- elif chatbot_name == "chatbot_jutor":
2372
  chatbot_jutor_visible = gr.update(visible=True)
 
2373
 
2374
- return chatbot_select_accordion_visible, chatbot_open_ai_visible, chatbot_open_ai_streaming_visible, chatbot_jutor_visible
 
 
 
 
 
 
 
 
 
 
 
 
 
2375
 
2376
  # --- Slide mode ---
2377
  def update_slide(direction):
@@ -2538,6 +2633,28 @@ HEAD = """
2538
  }
2539
  }
2540
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2541
  """
2542
 
2543
  with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, secondary_hue=gr.themes.colors.amber, text_size = gr.themes.sizes.text_lg), head=HEAD) as demo:
@@ -2556,8 +2673,12 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2556
  key_moments_state = gr.State() # 使用 gr.State 存储 key_moments
2557
  streaming_chat_thread_id_state = gr.State() # 使用 gr.State 存储 streaming_chat_thread_id
2558
  with gr.Tab("AI小精靈"):
2559
- with gr.Accordion("選擇 AI 小精靈", open=True) as chatbot_select_accordion:
 
 
2560
  with gr.Row():
 
 
2561
  with gr.Column(scale=1, variant="panel", visible=False):
2562
  chatbot_avatar_url = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
2563
  chatbot_description = """Hi,我是你的AI學伴【飛特精靈】,\n
@@ -2585,22 +2706,52 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2585
  chatbot_open_ai_streaming_select_btn = gr.Button("👆選擇【飛特音速】", elem_id="streaming_chatbot_btn", visible=True, variant="primary")
2586
  gr.Markdown(value=streaming_chatbot_description, visible=True)
2587
  with gr.Column(scale=1, variant="panel"):
2588
- 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"
2589
- jutor_chatbot_description = """Hi,我們是【梨梨、麥麥、狐狸貓】,\n
 
2590
  也可以陪你一起學習本次的內容,有什麼問題都可以問我喔!\n
2591
  🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!\n
2592
  🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!\n
2593
  🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!\n
2594
- 💤 精靈們體力都有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!\n
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2595
  """
2596
- chatbot_jutor_name = gr.State("chatbot_jutor")
2597
- gr.Image(value=jutor_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
2598
- chatbot_jutor_select_btn = gr.Button("👆選擇【梨梨、麥麥、狐狸貓】", elem_id="jutor_chatbot_btn", visible=True, variant="primary")
2599
- gr.Markdown(value=jutor_chatbot_description, visible=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2600
 
2601
  with gr.Row("飛特精靈") as chatbot_open_ai:
2602
  with gr.Column():
2603
- user_avatar = "https://em-content.zobj.net/source/google/263/flushed-face_1f633.png"
2604
  bot_avatar = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
2605
  latex_delimiters = [{"left": "$", "right": "$", "display": False}]
2606
  chatbot_greeting = [[
@@ -2659,14 +2810,17 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2659
  💤 精靈們體力都有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!
2660
  """,
2661
  ]]
2662
- 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"
2663
- ai_name = gr.Dropdown(label="選擇 AI 助理", choices=[
2664
- # ("梨梨","jutor"),
2665
- ("麥麥","claude3"),
2666
- ("狐狸貓","groq")],
2667
- value="claude3"
 
 
 
2668
  )
2669
- 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)
2670
  ai_chatbot_socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
2671
  with gr.Row():
2672
  with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion_2:
@@ -2700,7 +2854,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2700
  with gr.Row():
2701
  worksheet_content_type_name = gr.Textbox(value="worksheet", visible=False)
2702
  worksheet_algorithm = gr.Dropdown(label="選擇教學策略或理論", choices=["Bloom認知階層理論", "Polya數學解題法", "CRA教學法"], value="Bloom認知階層理論", visible=False)
2703
- worksheet_content_btn = gr.Button("生成學習單 📄", variant="primary", visible=False)
2704
  with gr.Accordion("微調", open=False):
2705
  worksheet_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2706
  worksheet_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
@@ -2721,7 +2875,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2721
  with gr.Row():
2722
  lesson_plan_content_type_name = gr.Textbox(value="lesson_plan", visible=False)
2723
  lesson_plan_time = gr.Slider(label="選擇課程時間(分鐘)", minimum=10, maximum=120, step=5, value=40)
2724
- lesson_plan_btn = gr.Button("生成教案 📕", variant="primary", visible=False)
2725
  with gr.Accordion("微調", open=False):
2726
  lesson_plan_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2727
  lesson_plan_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
@@ -2742,7 +2896,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2742
  with gr.Row():
2743
  exit_ticket_content_type_name = gr.Textbox(value="exit_ticket", visible=False)
2744
  exit_ticket_time = gr.Slider(label="選擇出場券時間(分鐘)", minimum=5, maximum=10, step=1, value=8)
2745
- exit_ticket_btn = gr.Button("生成出場券 🎟️", variant="primary", visible=False)
2746
  with gr.Accordion("微調", open=False):
2747
  exit_ticket_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2748
  exit_ticket_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
@@ -2861,25 +3015,58 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2861
  mind_map_html = gr.HTML()
2862
 
2863
  # --- Event ---
2864
- # CHATBOT SELECT
 
 
2865
  chatbot_open_ai_select_btn.click(
2866
  chatbot_select,
2867
  inputs=[chatbot_open_ai_name],
2868
- outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
2869
  )
2870
  chatbot_open_ai_streaming_select_btn.click(
2871
  chatbot_select,
2872
  inputs=[chatbot_open_ai_streaming_name],
2873
- outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
2874
  ).then(
2875
  create_thread_id,
2876
  inputs=[],
2877
  outputs=[streaming_chat_thread_id_state]
2878
  )
2879
- chatbot_jutor_select_btn.click(
 
 
 
 
 
 
 
 
 
 
2880
  chatbot_select,
2881
- inputs=[chatbot_jutor_name],
2882
- outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2883
  )
2884
 
2885
  # OPENAI ASSISTANT CHATBOT 模式
@@ -2895,31 +3082,24 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2895
  outputs=[msg]
2896
  )
2897
  # OPENAI ASSISTANT CHATBOT 連接按鈕點擊事件
2898
- 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, questions_answers_json, ai_chatbot_socratic_mode_btn]
2899
- 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, questions_answers_json, ai_chatbot_socratic_mode_btn]
2900
- 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, questions_answers_json, ai_chatbot_socratic_mode_btn]
2901
- btn_1.click(
2902
- chat_with_opan_ai_assistant,
2903
- inputs=btn_1_chat_with_opan_ai_assistant_input,
2904
- outputs=[msg, chatbot, thread_id],
2905
- scroll_to_output=True
2906
- )
2907
- btn_2.click(
2908
- chat_with_opan_ai_assistant,
2909
- inputs=btn_2_chat_with_opan_ai_assistant_input,
2910
- outputs=[msg, chatbot, thread_id],
2911
- scroll_to_output=True
2912
- )
2913
- btn_3.click(
2914
- chat_with_opan_ai_assistant,
2915
- inputs=btn_3_chat_with_opan_ai_assistant_input,
2916
- outputs=[msg, chatbot, thread_id],
2917
- scroll_to_output=True
2918
  )
 
 
 
 
 
 
 
2919
  btn_create_question.click(
2920
  change_questions,
2921
- inputs = [password, df_string_output],
2922
- outputs = [btn_1, btn_2, btn_3]
2923
  )
2924
 
2925
  # 其他精靈 ai_chatbot 模式
@@ -2930,27 +3110,11 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
2930
  scroll_to_output=True
2931
  )
2932
  # 其他精靈 ai_chatbot 连接按钮点击事件
2933
- 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, questions_answers_json, ai_chatbot_socratic_mode_btn]
2934
- 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, questions_answers_json, ai_chatbot_socratic_mode_btn]
2935
- 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, questions_answers_json, ai_chatbot_socratic_mode_btn]
2936
- ai_chatbot_question_1.click(
2937
- chat_with_ai,
2938
- inputs=ai_chatbot_question_1_chat_with_ai_input,
2939
- outputs=[ai_msg, ai_chatbot],
2940
- scroll_to_output=True
2941
- )
2942
- ai_chatbot_question_2.click(
2943
- chat_with_ai,
2944
- inputs=ai_chatbot_question_2_chat_with_ai_input,
2945
- outputs=[ai_msg, ai_chatbot],
2946
- scroll_to_output=True
2947
- )
2948
- ai_chatbot_question_3.click(
2949
- chat_with_ai,
2950
- inputs=ai_chatbot_question_3_chat_with_ai_input,
2951
- outputs=[ai_msg, ai_chatbot],
2952
- scroll_to_output=True
2953
- )
2954
 
2955
  # file_upload.change(process_file, inputs=file_upload, outputs=df_string_output)
2956
  # file_upload.change(process_file, inputs=file_upload, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
@@ -3016,246 +3180,301 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
3016
  inputs=update_state_inputs,
3017
  outputs=update_state_outputs
3018
  )
3019
-
3020
 
3021
- # 当输入网页链接时触发
3022
- # web_link.change(process_web_link, inputs=web_link, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
3023
-
3024
- # reading_passage event
3025
- # reading_passage_text_to_latex.click(
3026
- # reading_passage_add_latex_version,
3027
- # inputs=[video_id],
3028
- # outputs=[reading_passage_text]
3029
- # )
3030
- reading_passage_get_button.click(
3031
- get_LLM_content,
3032
- inputs=[video_id, reading_passage_kind],
3033
- outputs=[reading_passage_text]
3034
- )
3035
- reading_passage_create_button.click(
3036
- create_LLM_content,
3037
- inputs=[video_id, df_string_output, reading_passage_kind],
3038
- outputs=[reading_passage_text]
3039
- )
3040
- reading_passage_delete_button.click(
3041
- delete_LLM_content,
3042
- inputs=[video_id, reading_passage_kind],
3043
- outputs=[reading_passage_text]
3044
- )
3045
- reading_passage_edit_button.click(
3046
- enable_edit_mode,
3047
- inputs=[],
3048
- outputs=[reading_passage_text]
3049
- )
3050
- reading_passage_update_button.click(
3051
- update_LLM_content,
3052
- inputs=[video_id, reading_passage_text, reading_passage_kind],
3053
- outputs=[reading_passage_text]
3054
- )
3055
 
3056
- # summary event
3057
- # summary_to_markdown.click(
3058
- # summary_add_markdown_version,
3059
- # inputs=[video_id],
3060
- # outputs=[summary_text]
3061
- # )
3062
- summary_get_button.click(
3063
- get_LLM_content,
3064
- inputs=[video_id, summary_kind],
3065
- outputs=[summary_text]
3066
- )
3067
- summary_create_button.click(
3068
- create_LLM_content,
3069
- inputs=[video_id, df_string_output, summary_kind],
3070
- outputs=[summary_text]
3071
- )
3072
- summary_delete_button.click(
3073
- delete_LLM_content,
3074
- inputs=[video_id, summary_kind],
3075
- outputs=[summary_text]
3076
- )
3077
- summary_edit_button.click(
3078
- enable_edit_mode,
3079
- inputs=[],
3080
- outputs=[summary_text]
3081
- )
3082
- summary_update_button.click(
3083
- update_LLM_content,
3084
- inputs=[video_id, summary_text, summary_kind],
3085
- outputs=[summary_text]
3086
- )
3087
-
3088
- # transcript event
3089
- transcript_get_button.click(
3090
- get_LLM_content,
3091
- inputs=[video_id, transcript_kind],
3092
- outputs=[df_string_output]
3093
- )
3094
- transcript_create_button.click(
3095
- create_LLM_content,
3096
- inputs=[video_id, df_string_output, transcript_kind],
3097
- outputs=[df_string_output]
3098
- )
3099
- transcript_delete_button.click(
3100
- delete_LLM_content,
3101
- inputs=[video_id, transcript_kind],
3102
- outputs=[df_string_output]
3103
- )
3104
- transcript_edit_button.click(
3105
- enable_edit_mode,
3106
- inputs=[],
3107
- outputs=[df_string_output]
3108
- )
3109
- transcript_update_button.click(
3110
- update_LLM_content,
3111
- inputs=[video_id, df_string_output, transcript_kind],
3112
- outputs=[df_string_output]
3113
- )
3114
-
3115
- # key_moments event
3116
- key_moments_get_button.click(
3117
- get_LLM_content,
3118
- inputs=[video_id, key_moments_kind],
3119
- outputs=[key_moments]
3120
- )
3121
- key_moments_create_button.click(
3122
- create_LLM_content,
3123
- inputs=[video_id, df_string_output, key_moments_kind],
3124
- outputs=[key_moments]
3125
- )
3126
- key_moments_delete_button.click(
3127
- delete_LLM_content,
3128
- inputs=[video_id, key_moments_kind],
3129
- outputs=[key_moments]
3130
- )
3131
- key_moments_edit_button.click(
3132
- enable_edit_mode,
3133
- inputs=[],
3134
- outputs=[key_moments]
3135
- )
3136
- key_moments_update_button.click(
3137
- update_LLM_content,
3138
- inputs=[video_id, key_moments, key_moments_kind],
3139
- outputs=[key_moments]
3140
- )
3141
-
3142
- # question_list event
3143
- questions_get_button.click(
3144
- get_LLM_content,
3145
- inputs=[video_id, questions_kind],
3146
- outputs=[questions_json]
3147
- )
3148
- questions_create_button.click(
3149
- create_LLM_content,
3150
- inputs=[video_id, df_string_output, questions_kind],
3151
- outputs=[questions_json]
3152
- )
3153
- questions_delete_button.click(
3154
- delete_LLM_content,
3155
- inputs=[video_id, questions_kind],
3156
- outputs=[questions_json]
3157
- )
3158
- questions_edit_button.click(
3159
- enable_edit_mode,
3160
- inputs=[],
3161
- outputs=[questions_json]
3162
- )
3163
- questions_update_button.click(
3164
- update_LLM_content,
3165
- inputs=[video_id, questions_json, questions_kind],
3166
- outputs=[questions_json]
3167
- )
3168
- # questions_answers event
3169
- questions_answers_get_button.click(
3170
- get_LLM_content,
3171
- inputs=[video_id, questions_answers_kind],
3172
- outputs=[questions_answers_json]
3173
- )
3174
- questions_answers_create_button.click(
3175
- create_LLM_content,
3176
- inputs=[video_id, df_string_output, questions_answers_kind],
3177
- outputs=[questions_answers_json]
3178
- )
3179
- questions_answers_delete_button.click(
3180
- delete_LLM_content,
3181
- inputs=[video_id, questions_answers_kind],
3182
- outputs=[questions_answers_json]
3183
- )
3184
- questions_answers_edit_button.click(
3185
- enable_edit_mode,
3186
- inputs=[],
3187
- outputs=[questions_answers_json]
3188
- )
3189
- questions_answers_update_button.click(
3190
- update_LLM_content,
3191
- inputs=[video_id, questions_answers_json, questions_answers_kind],
3192
- outputs=[questions_answers_json]
3193
- )
3194
-
3195
-
3196
- # 教師版
3197
- worksheet_content_btn.click(
3198
- get_ai_content,
3199
- inputs=[password, video_id, df_string_output, content_subject, content_grade, content_level, worksheet_algorithm, worksheet_content_type_name],
3200
- outputs=[worksheet_exam_result_original, worksheet_exam_result, worksheet_prompt, worksheet_exam_result_prompt]
3201
- )
3202
- lesson_plan_btn.click(
3203
- get_ai_content,
3204
- inputs=[password, video_id, df_string_output, content_subject, content_grade, content_level, lesson_plan_time, lesson_plan_content_type_name],
3205
- outputs=[lesson_plan_exam_result_original, lesson_plan_exam_result, lesson_plan_prompt, lesson_plan_exam_result_prompt]
3206
- )
3207
- exit_ticket_btn.click(
3208
- get_ai_content,
3209
- inputs=[password, video_id, df_string_output, content_subject, content_grade, content_level, exit_ticket_time, exit_ticket_content_type_name],
3210
- outputs=[exit_ticket_exam_result_original, exit_ticket_exam_result, exit_ticket_prompt, exit_ticket_exam_result_prompt]
3211
- )
3212
 
3213
- # 生成結果微調
3214
- worksheet_exam_result_fine_tune_btn.click(
3215
- generate_exam_fine_tune_result,
3216
- inputs=[password, worksheet_exam_result_prompt, df_string_output, worksheet_exam_result, worksheet_exam_result_fine_tune_prompt],
3217
- outputs=[worksheet_exam_result]
3218
- )
3219
- worksheet_download_exam_result_button.click(
3220
- download_exam_result,
3221
- inputs=[worksheet_exam_result],
3222
- outputs=[worksheet_exam_result_word_link]
3223
- )
3224
- worksheet_exam_result_retrun_original.click(
3225
- return_original_exam_result,
3226
- inputs=[worksheet_exam_result_original],
3227
- outputs=[worksheet_exam_result]
3228
- )
3229
- lesson_plan_exam_result_fine_tune_btn.click(
3230
- generate_exam_fine_tune_result,
3231
- inputs=[password, lesson_plan_exam_result_prompt, df_string_output, lesson_plan_exam_result, lesson_plan_exam_result_fine_tune_prompt],
3232
- outputs=[lesson_plan_exam_result]
3233
- )
3234
- lesson_plan_download_exam_result_button.click(
3235
- download_exam_result,
3236
- inputs=[lesson_plan_exam_result],
3237
- outputs=[lesson_plan_exam_result_word_link]
3238
- )
3239
- lesson_plan_exam_result_retrun_original.click(
3240
- return_original_exam_result,
3241
- inputs=[lesson_plan_exam_result_original],
3242
- outputs=[lesson_plan_exam_result]
3243
- )
3244
- exit_ticket_exam_result_fine_tune_btn.click(
3245
- generate_exam_fine_tune_result,
3246
- inputs=[password, exit_ticket_exam_result_prompt, df_string_output, exit_ticket_exam_result, exit_ticket_exam_result_fine_tune_prompt],
3247
- outputs=[exit_ticket_exam_result]
3248
- )
3249
- exit_ticket_download_exam_result_button.click(
3250
- download_exam_result,
3251
- inputs=[exit_ticket_exam_result],
3252
- outputs=[exit_ticket_exam_result_word_link]
3253
- )
3254
- exit_ticket_exam_result_retrun_original.click(
3255
- return_original_exam_result,
3256
- inputs=[exit_ticket_exam_result_original],
3257
- outputs=[exit_ticket_exam_result]
3258
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3259
 
3260
  # init_params
3261
  init_outputs = [
 
92
  else:
93
  raise gr.Error("密碼錯誤")
94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  # # ====drive====初始化
96
  def init_drive_service():
97
  credentials_json_string = DRIVE_KEY
 
291
  return None
292
 
293
  def get_transcript_by_yt_api(video_id):
294
+ transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)
295
+ languages = []
296
+ for t in transcript_list:
297
+ languages.append(t.language_code)
298
+
299
  for language in languages:
300
  try:
301
  transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=[language])
 
397
  transcript = generate_transcription_by_whisper(video_id)
398
 
399
  transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
400
+ GCS_SERVICE.upload_json_string(bucket_name, transcript_blob_name, transcript_text)
401
+
402
  is_new_transcript = True
403
  else:
404
  # 逐字稿已存在,下载逐字稿内容
405
  print("逐字稿已存在于GCS中")
406
+ transcript_text = GCS_SERVICE.download_as_string(bucket_name, transcript_blob_name)
407
  transcript = json.loads(transcript_text)
408
 
409
  # print("===確認其他衍生文件===")
 
432
  # 截图
433
  screenshot_path = screenshot_youtube_video(video_id, entry['start'])
434
  screenshot_blob_name = f"{video_id}/{video_id}_{entry['start']}.jpg"
435
+ img_file_id = GCS_SERVICE.upload_image_and_get_public_url(bucket_name, screenshot_blob_name, screenshot_path)
436
  entry['img_file_id'] = img_file_id
437
  print(f"截图已上传到GCS: {img_file_id}")
438
  is_new_transcript = True
 
444
  print(transcript)
445
  print("===更新逐字稿文件===")
446
  updated_transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
447
+ GCS_SERVICE.upload_json_string(bucket_name, transcript_blob_name, updated_transcript_text)
448
  print("逐字稿已更新,包括截图链接")
449
  updated_transcript_json = json.loads(updated_transcript_text)
450
  else:
 
638
  reading_passage = generate_reading_passage(df_string)
639
  reading_passage_json = {"reading_passage": str(reading_passage)}
640
  reading_passage_text = json.dumps(reading_passage_json, ensure_ascii=False, indent=2)
641
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, reading_passage_text)
642
  print("reading_passage已上传到GCS")
643
  else:
644
  # reading_passage已存在,下载内容
645
  print("reading_passage已存在于GCS中")
646
+ reading_passage_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
647
  reading_passage_json = json.loads(reading_passage_text)
648
 
649
  elif source == "drive":
 
682
  敘述中,請把數學或是專業術語,用 Latex 包覆($...$),並且不要去改原本的文章
683
  加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
684
  """
 
 
 
 
685
 
686
+ try:
687
+ # 使用 OPEN AI 生成 Reading Passage
688
+ messages = [
689
+ {"role": "system", "content": sys_content},
690
+ {"role": "user", "content": user_content}
691
+ ]
692
 
693
+ request_payload = {
694
+ "model": "gpt-4-turbo",
695
+ "messages": messages,
696
+ "max_tokens": 4000,
697
+ }
698
+
699
+ response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
700
+ reading_passage = response.choices[0].message.content.strip()
701
+ except:
702
+ # 使用 REDROCK 生成 Reading Passage
703
+ messages = [
704
+ {"role": "user", "content": user_content}
705
+ ]
706
+ model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
707
+ # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
708
+ kwargs = {
709
+ "modelId": model_id,
710
+ "contentType": "application/json",
711
+ "accept": "application/json",
712
+ "body": json.dumps({
713
+ "anthropic_version": "bedrock-2023-05-31",
714
+ "max_tokens": 4000,
715
+ "system": sys_content,
716
+ "messages": messages
717
+ })
718
+ }
719
+ response = BEDROCK_CLIENT.invoke_model(**kwargs)
720
+ response_body = json.loads(response.get('body').read())
721
+ reading_passage = response_body.get('content')[0].get('text')
722
+
723
  print("=====reading_passage=====")
724
  print(reading_passage)
725
  print("=====reading_passage=====")
 
745
  mind_map = generate_mind_map(df_string)
746
  mind_map_json = {"mind_map": str(mind_map)}
747
  mind_map_text = json.dumps(mind_map_json, ensure_ascii=False, indent=2)
748
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, mind_map_text)
749
  print("mind_map已上傳到GCS")
750
  else:
751
  # mindmap已存在,下载内容
752
  print("mind_map已存在于GCS中")
753
+ mind_map_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
754
  mind_map_json = json.loads(mind_map_text)
755
 
756
  elif source == "drive":
 
784
  注意:不需要前後文敘述,直接給出 markdown 文本即可
785
  這對我很重要
786
  """
 
 
 
 
787
 
788
+ try:
789
+ # 使用 OPEN AI 生成
790
+ messages = [
791
+ {"role": "system", "content": sys_content},
792
+ {"role": "user", "content": user_content}
793
+ ]
794
 
795
+ request_payload = {
796
+ "model": "gpt-4-turbo",
797
+ "messages": messages,
798
+ "max_tokens": 4000,
799
+ }
800
+
801
+ response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
802
+ mind_map = response.choices[0].message.content.strip()
803
+ except:
804
+ # 使用 REDROCK 生成
805
+ messages = [
806
+ {"role": "user", "content": user_content}
807
+ ]
808
+ model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
809
+ # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
810
+ kwargs = {
811
+ "modelId": model_id,
812
+ "contentType": "application/json",
813
+ "accept": "application/json",
814
+ "body": json.dumps({
815
+ "anthropic_version": "bedrock-2023-05-31",
816
+ "max_tokens": 4000,
817
+ "system": sys_content,
818
+ "messages": messages
819
+ })
820
+ }
821
+ response = BEDROCK_CLIENT.invoke_model(**kwargs)
822
+ response_body = json.loads(response.get('body').read())
823
+ mind_map = response_body.get('content')[0].get('text')
824
  print("=====mind_map=====")
825
  print(mind_map)
826
  print("=====mind_map=====")
 
853
  summary = generate_summarise(df_string, meta_data)
854
  summary_json = {"summary": str(summary)}
855
  summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
856
+ GCS_SERVICE.upload_json_string(bucket_name, summary_file_blob_name, summary_text)
857
  print("summary已上传到GCS")
858
  else:
859
  # summary已存在,下载内容
860
  print("summary已存在于GCS中")
861
+ summary_text = GCS_SERVICE.download_as_string(bucket_name, summary_file_blob_name)
862
  summary_json = json.loads(summary_text)
863
 
864
  elif source == "drive":
 
944
  # 💡 5. 結論反思(為什麼我們要學這個?)
945
  # ❓ 6. 延伸小問題
946
 
947
+ try:
948
+ #OPEN AI
949
+ messages = [
950
+ {"role": "system", "content": sys_content},
951
+ {"role": "user", "content": user_content}
952
+ ]
953
+
954
+ request_payload = {
955
+ "model": "gpt-4-turbo",
956
+ "messages": messages,
957
+ "max_tokens": 4000,
958
+ }
959
+
960
+ response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
961
+ df_summarise = response.choices[0].message.content.strip()
962
+ except:
963
+ #REDROCK
964
+ messages = [
965
+ {"role": "user", "content": user_content}
966
+ ]
967
+ model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
968
+ # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
969
+ kwargs = {
970
+ "modelId": model_id,
971
+ "contentType": "application/json",
972
+ "accept": "application/json",
973
+ "body": json.dumps({
974
+ "anthropic_version": "bedrock-2023-05-31",
975
+ "max_tokens": 4000,
976
+ "system": sys_content,
977
+ "messages": messages
978
+ })
979
+ }
980
+ response = BEDROCK_CLIENT.invoke_model(**kwargs)
981
+ response_body = json.loads(response.get('body').read())
982
+ df_summarise = response_body.get('content')[0].get('text')
983
 
 
 
 
 
 
984
 
 
 
985
  print("=====df_summarise=====")
986
  print(df_summarise)
987
  print("=====df_summarise=====")
 
1001
  if not is_questions_exists:
1002
  questions = generate_questions(df_string)
1003
  questions_text = json.dumps(questions, ensure_ascii=False, indent=2)
1004
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, questions_text)
1005
  print("questions已上傳到GCS")
1006
  else:
1007
  # 逐字稿已存在,下载逐字稿内容
1008
  print("questions已存在于GCS中")
1009
+ questions_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1010
  questions = json.loads(questions_text)
1011
 
1012
  elif source == "drive":
 
1053
 
1054
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,並用既有資料為本質猜測用戶可能會問的問題,使用 zh-TW"
1055
  user_content = f"請根據 {content_text} 生成三個問題,並用 JSON 格式返回 questions:[q1的敘述text, q2的敘述text, q3的敘述text]"
1056
+
1057
+ try:
1058
+ messages = [
1059
+ {"role": "system", "content": sys_content},
1060
+ {"role": "user", "content": user_content}
1061
+ ]
1062
+ response_format = { "type": "json_object" }
1063
 
1064
+ print("=====messages=====")
1065
+ print(messages)
1066
+ print("=====messages=====")
1067
 
1068
 
1069
+ request_payload = {
1070
+ "model": "gpt-4-turbo",
1071
+ "messages": messages,
1072
+ "max_tokens": 4000,
1073
+ "response_format": response_format
1074
+ }
1075
+
1076
+ response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1077
+ questions = json.loads(response.choices[0].message.content)["questions"]
1078
+ except:
1079
+ messages = [
1080
+ {"role": "user", "content": user_content}
1081
+ ]
1082
+ model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
1083
+ # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
1084
+ kwargs = {
1085
+ "modelId": model_id,
1086
+ "contentType": "application/json",
1087
+ "accept": "application/json",
1088
+ "body": json.dumps({
1089
+ "anthropic_version": "bedrock-2023-05-31",
1090
+ "max_tokens": 4000,
1091
+ "system": sys_content,
1092
+ "messages": messages
1093
+ })
1094
+ }
1095
+ response = BEDROCK_CLIENT.invoke_model(**kwargs)
1096
+ response_body = json.loads(response.get('body').read())
1097
+ response_completion = response_body.get('content')[0].get('text')
1098
+ questions = json.loads(response_completion)["questions"]
1099
 
 
 
1100
  print("=====json_response=====")
1101
  print(questions)
1102
  print("=====json_response=====")
 
1116
  if not is_questions_answers_exists:
1117
  questions_answers = generate_questions_answers(df_string)
1118
  questions_answers_text = json.dumps(questions_answers, ensure_ascii=False, indent=2)
1119
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, questions_answers_text)
1120
  print("questions_answers已上傳到GCS")
1121
  else:
1122
  # questions_answers已存在,下载内容
1123
  print("questions_answers已存在于GCS中")
1124
+ questions_answers_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1125
  questions_answers = json.loads(questions_answers_text)
1126
  except:
1127
  questions = get_questions(video_id, df_string, source)
 
1215
  key_moments = generate_key_moments(formatted_simple_transcript, formatted_transcript)
1216
  key_moments_json = {"key_moments": key_moments}
1217
  key_moments_text = json.dumps(key_moments_json, ensure_ascii=False, indent=2)
1218
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, key_moments_text)
1219
  print("key_moments已上傳到GCS")
1220
  else:
1221
  # key_moments已存在,下载内容
1222
  print("key_moments已存在于GCS中")
1223
+ key_moments_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1224
  key_moments_json = json.loads(key_moments_text)
1225
  # 檢查 key_moments 是否有 keywords
1226
  print("===檢查 key_moments 是否有 keywords===")
 
1235
  has_keywords_added = True
1236
  if has_keywords_added:
1237
  key_moments_text = json.dumps(key_moments_json, ensure_ascii=False, indent=2)
1238
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, key_moments_text)
1239
+ key_moments_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1240
  key_moments_json = json.loads(key_moments_text)
1241
 
1242
  elif source == "drive":
 
1284
  "keywords": ["關鍵字", "關鍵字"]
1285
  }}]
1286
  """
 
 
 
 
 
 
 
 
 
 
 
 
1287
 
1288
  try:
1289
+ #OPEN AI
1290
+ messages = [
1291
+ {"role": "system", "content": sys_content},
1292
+ {"role": "user", "content": user_content}
1293
+ ]
1294
+ response_format = { "type": "json_object" }
1295
+
1296
+ request_payload = {
1297
+ "model": "gpt-4-turbo",
1298
+ "messages": messages,
1299
+ "max_tokens": 4096,
1300
+ "response_format": response_format
1301
+ }
1302
+
1303
  response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1304
  print("===response===")
1305
  print(dict(response))
1306
  key_moments = json.loads(response.choices[0].message.content)["key_moments"]
1307
  except Exception as e:
1308
+ error_msg = f" {video_id} OPEN AI 關鍵時刻錯誤: {str(e)}"
1309
  print("===generate_key_moments error===")
1310
  print(error_msg)
1311
  print("===generate_key_moments error===")
1312
+
1313
+ #REDROCK
1314
+ messages = [
1315
+ {"role": "user", "content": user_content}
1316
+ ]
1317
+ model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
1318
+ # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
1319
+ kwargs = {
1320
+ "modelId": model_id,
1321
+ "contentType": "application/json",
1322
+ "accept": "application/json",
1323
+ "body": json.dumps({
1324
+ "anthropic_version": "bedrock-2023-05-31",
1325
+ "max_tokens": 4096,
1326
+ "system": sys_content,
1327
+ "messages": messages
1328
+ })
1329
+ }
1330
+ response = BEDROCK_CLIENT.invoke_model(**kwargs)
1331
+ response_body = json.loads(response.get('body').read())
1332
+ response_completion = response_body.get('content')[0].get('text')
1333
+ key_moments = json.loads(response_completion)["key_moments"]
1334
 
1335
  print("=====key_moments=====")
1336
  print(key_moments)
 
1354
  不用給上下文,直接給出關鍵字,使用 zh-TW,用逗號分隔, example: 關鍵字1, 關鍵字2
1355
  transcript:{transcript}
1356
  """
 
 
 
 
 
 
 
 
 
1357
 
1358
+ try:
1359
+ # OPEN AI
1360
+ messages = [
1361
+ {"role": "system", "content": system_content},
1362
+ {"role": "user", "content": user_content}
1363
+ ]
1364
+ request_payload = {
1365
+ "model": "gpt-4-turbo",
1366
+ "messages": messages,
1367
+ "max_tokens": 100,
1368
+ }
1369
+
1370
+ response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1371
+ keywords = response.choices[0].message.content.strip().split(", ")
1372
+ except:
1373
+ # REDROCK
1374
+ messages = [
1375
+ {"role": "user", "content": user_content}
1376
+ ]
1377
+ model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
1378
+ # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
1379
+ kwargs = {
1380
+ "modelId": model_id,
1381
+ "contentType": "application/json",
1382
+ "accept": "application/json",
1383
+ "body": json.dumps({
1384
+ "anthropic_version": "bedrock-2023-05-31",
1385
+ "max_tokens": 100,
1386
+ "system": system_content,
1387
+ "messages": messages
1388
+ })
1389
+ }
1390
+ response = BEDROCK_CLIENT.invoke_model(**kwargs)
1391
+ response_body = json.loads(response.get('body').read())
1392
+ response_completion = response_body.get('content')[0].get('text')
1393
+ keywords = response_completion.strip().split(", ")
1394
 
1395
  return keywords
1396
 
 
1476
  background-color: black;
1477
  border-radius: 50%;
1478
  text-decoration: none;
1479
+ color: white !important;
1480
  opacity: 0.8;
1481
  transition: opacity 200ms ease;
1482
  }
 
1564
  image_elements += f"""
1565
  <div id="{current_id}" class="gallery__item">
1566
  <a href="#{prev_id}" class="click-zone click-zone-prev">
1567
+ <div class="arrow arrow-disabled arrow-prev"> ◀︎ </div>
1568
  </a>
1569
  <a href="#{next_id}" class="click-zone click-zone-next">
1570
+ <div class="arrow arrow-next"> ▶︎ </div>
1571
  </a>
1572
  <img src="{image}">
1573
  </div>
 
1606
  # 检查 file 是否存在
1607
  is_file_exists = GCS_SERVICE.check_file_exists(bucket_name, blob_name)
1608
  if is_file_exists:
1609
+ content = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1610
  content_json = json.loads(content)
1611
  if kind == "reading_passage_latex":
1612
  content_text = content_json["reading_passage"]
 
1630
  # 检查 file 是否存在
1631
  is_file_exists = GCS_SERVICE.check_file_exists(bucket_name, blob_name)
1632
  if is_file_exists:
1633
+ GCS_SERVICE.delete_blob(bucket_name, blob_name)
1634
  print(f"{file_name}已从GCS中删除")
1635
  return gr.update(value="", interactive=False)
1636
 
 
1646
  print(new_content)
1647
  reading_passage_json = {"reading_passage": str(new_content)}
1648
  reading_passage_text = json.dumps(reading_passage_json, ensure_ascii=False, indent=2)
1649
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, reading_passage_text)
1650
  updated_content = new_content
1651
  elif kind == "summary_markdown":
1652
  summary_json = {"summary": str(new_content)}
1653
  summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
1654
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, summary_text)
1655
  updated_content = new_content
1656
  elif kind == "mind_map":
1657
  mind_map_json = {"mind_map": str(new_content)}
1658
  mind_map_text = json.dumps(mind_map_json, ensure_ascii=False, indent=2)
1659
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, mind_map_text)
1660
  updated_content = mind_map_text
1661
  elif kind == "key_moments":
1662
  # from update_LLM_btn -> new_content is a string
 
1667
  key_moments_list = new_content
1668
  key_moments_json = {"key_moments": key_moments_list}
1669
  key_moments_text = json.dumps(key_moments_json, ensure_ascii=False, indent=2)
1670
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, key_moments_text)
1671
  updated_content = key_moments_text
1672
  elif kind == "transcript":
1673
  if isinstance(new_content, str):
 
1675
  else:
1676
  transcript_json = new_content
1677
  transcript_text = json.dumps(transcript_json, ensure_ascii=False, indent=2)
1678
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, transcript_text)
1679
  updated_content = transcript_text
1680
  elif kind == "questions":
1681
  # from update_LLM_btn -> new_content is a string
 
1685
  else:
1686
  questions_json = new_content
1687
  questions_text = json.dumps(questions_json, ensure_ascii=False, indent=2)
1688
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, questions_text)
1689
  updated_content = questions_text
1690
  elif kind == "questions_answers":
1691
  # from update_LLM_btn -> new_content is a string
 
1695
  else:
1696
  questions_answers_json = new_content
1697
  questions_answers_text = json.dumps(questions_answers_json, ensure_ascii=False, indent=2)
1698
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, questions_answers_text)
1699
  updated_content = questions_answers_text
1700
 
1701
  print(f"{kind} 已更新到GCS")
 
1749
  def reading_passage_add_latex_version(video_id):
1750
  # 確認 GCS 是否有 reading_passage.json
1751
  print("===reading_passage_convert_to_latex===")
 
1752
  bucket_name = 'video_ai_assistant'
1753
  file_name = f'{video_id}_reading_passage.json'
1754
  blob_name = f"{video_id}/{file_name}"
 
1761
 
1762
  # 逐字稿已存在,下载逐字稿内容
1763
  print("reading_passage 已存在于GCS中,轉換 Latex 模式")
1764
+ reading_passage_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1765
  reading_passage_json = json.loads(reading_passage_text)
1766
  original_reading_passage = reading_passage_json["reading_passage"]
1767
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
 
1794
  # 另存為 reading_passage_latex.json
1795
  new_file_name = f'{video_id}_reading_passage_latex.json'
1796
  new_blob_name = f"{video_id}/{new_file_name}"
1797
+ GCS_SERVICE.upload_json_string(bucket_name, new_blob_name, reading_passage_text)
1798
 
1799
  return new_reading_passage
1800
 
1801
  def summary_add_markdown_version(video_id):
1802
  # 確認 GCS 是否有 summary.json
1803
  print("===summary_convert_to_markdown===")
 
1804
  bucket_name = 'video_ai_assistant'
1805
  file_name = f'{video_id}_summary.json'
1806
  blob_name = f"{video_id}/{file_name}"
 
1813
 
1814
  # 逐字稿已存在,下载逐字稿内容
1815
  print("summary 已存在于GCS中,轉換 Markdown 模式")
1816
+ summary_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1817
  summary_json = json.loads(summary_text)
1818
  original_summary = summary_json["summary"]
1819
  sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
 
1862
  # 另存為 summary_markdown.json
1863
  new_file_name = f'{video_id}_summary_markdown.json'
1864
  new_blob_name = f"{video_id}/{new_file_name}"
1865
+ GCS_SERVICE.upload_json_string(bucket_name, new_blob_name, summary_text)
1866
 
1867
  return new_summary
1868
 
 
1886
  else:
1887
  # meta_data已存在,下载内容
1888
  print("meta_data已存在于GCS中")
1889
+ meta_data_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1890
  meta_data_json = json.loads(meta_data_text)
1891
 
1892
  # meta_data_json grade 數字轉換成文字
 
1914
  verify_password(password)
1915
  if source == "gcs":
1916
  print("===get_ai_content on gcs===")
 
1917
  bucket_name = 'video_ai_assistant'
1918
  file_name = f'{video_id}_ai_content_list.json'
1919
  blob_name = f"{video_id}/{file_name}"
 
1923
  # 先建立一個 ai_content_list.json
1924
  ai_content_list = []
1925
  ai_content_text = json.dumps(ai_content_list, ensure_ascii=False, indent=2)
1926
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, ai_content_text)
1927
  print("ai_content_list [] 已上傳到GCS")
1928
 
1929
  # 此時 ai_content_list 已存在
1930
+ ai_content_list_string = GCS_SERVICE.download_as_string(bucket_name, blob_name)
1931
  ai_content_list = json.loads(ai_content_list_string)
1932
  # by key 找到 ai_content (topic, grade, level, specific_feature, content_type)
1933
  target_kvs = {
 
1954
 
1955
  ai_content_list.append(ai_content_json)
1956
  ai_content_text = json.dumps(ai_content_list, ensure_ascii=False, indent=2)
1957
+ GCS_SERVICE.upload_json_string(bucket_name, blob_name, ai_content_text)
1958
  print("ai_content已上傳到GCS")
1959
  else:
1960
  ai_content_json = ai_content_json[-1]
 
1967
  verify_password(password)
1968
  material = EducationalMaterial(df_string, topic, grade, level, specific_feature, content_type)
1969
  prompt = material.generate_content_prompt()
1970
+ try:
1971
+ ai_content = material.get_ai_content(OPEN_AI_CLIENT, ai_type="openai")
1972
+ except Exception as e:
1973
+ error_msg = f" {video_id} OPEN AI 生成教學素材錯誤: {str(e)}"
1974
+ print("===generate_ai_content error===")
1975
+ print(error_msg)
1976
+ print("===generate_ai_content error===")
1977
+ ai_content = material.get_ai_content(BEDROCK_CLIENT, ai_type="bedrock")
1978
+
1979
  return ai_content, prompt
1980
 
1981
  def generate_exam_fine_tune_result(password, exam_result_prompt , df_string_output, exam_result, exam_result_fine_tune_prompt):
1982
  verify_password(password)
1983
  material = EducationalMaterial(df_string_output, "", "", "", "", "")
1984
+ try:
1985
+ fine_tuned_ai_content = material.get_fine_tuned_ai_content(OPEN_AI_CLIENT, "openai", exam_result_prompt, exam_result, exam_result_fine_tune_prompt)
1986
+ except:
1987
+ fine_tuned_ai_content = material.get_fine_tuned_ai_content(BEDROCK_CLIENT, "bedrock", exam_result_prompt, exam_result, exam_result_fine_tune_prompt)
1988
+
1989
+ return fine_tuned_ai_content
 
 
 
 
1990
 
1991
  def return_original_exam_result(exam_result_original):
1992
  return exam_result_original
 
2055
  error_msg = "此次對話超過上限(對話一輪10次)"
2056
  raise gr.Error(error_msg)
2057
 
2058
+ if not ai_name in ["foxcat", "lili", "maimai"]:
2059
+ ai_name = "foxcat"
2060
+
2061
+ # if ai_name == "jutor":
2062
+ # ai_client = ""
2063
+ # elif ai_name == "claude3":
2064
+ # ai_client = BEDROCK_CLIENT
2065
+ # elif ai_name == "groq":
2066
+ # ai_client = GROQ_CLIENT
2067
+ # else:
2068
+ # ai_client = ""
2069
+
2070
+ ai_name_clients_model = {
2071
+ "foxcat": {
2072
+ "ai_name": "foxcat",
2073
+ "ai_client": GROQ_CLIENT,
2074
+ "ai_model_name": "groq_llama3",
2075
+ },
2076
+ "lili": {
2077
+ "ai_name": "lili",
2078
+ "ai_client": BEDROCK_CLIENT,
2079
+ "ai_model_name": "claude3",
2080
+ },
2081
+ # "maimai": {
2082
+ # "ai_name": "maimai",
2083
+ # "ai_client": OPEN_AI_CLIENT,
2084
+ # "ai_model_name": "openai",
2085
+ # }
2086
+ "maimai": {
2087
+ "ai_name": "maimai",
2088
+ "ai_client": GROQ_CLIENT,
2089
+ "ai_model_name": "groq_mixtral",
2090
+ }
2091
+ }
2092
+ ai_client = ai_name_clients_model.get(ai_name, "foxcat")["ai_client"]
2093
+ ai_model_name = ai_name_clients_model.get(ai_name, "foxcat")["ai_model_name"]
2094
 
2095
  if isinstance(trascript_state, str):
2096
  simple_transcript = json.loads(trascript_state)
 
2117
  "content_subject": content_subject,
2118
  "content_grade": content_grade,
2119
  "jutor_chat_key": JUTOR_CHAT_KEY,
2120
+ "ai_model_name": ai_model_name,
2121
  "ai_client": ai_client,
2122
  "instructions": instructions
2123
  }
2124
 
2125
  try:
2126
  chatbot = Chatbot(chatbot_config)
2127
+ response_completion = chatbot.chat(user_message, chat_history, socratic_mode, ai_model_name)
2128
  except Exception as e:
2129
  print(f"Error: {e}")
2130
  response_completion = "學習精靈有點累,請稍後再試!"
 
2438
  return thread_id
2439
 
2440
  def chatbot_select(chatbot_name):
2441
+ chatbot_select_accordion_visible = gr.update(visible=False)
2442
+ all_chatbot_select_btn_visible = gr.update(visible=True)
2443
  chatbot_open_ai_visible = gr.update(visible=False)
2444
  chatbot_open_ai_streaming_visible = gr.update(visible=False)
2445
  chatbot_jutor_visible = gr.update(visible=False)
2446
+ ai_name_update = gr.update(value="foxcat")
2447
 
2448
  if chatbot_name == "chatbot_open_ai":
2449
  chatbot_open_ai_visible = gr.update(visible=True)
2450
  elif chatbot_name == "chatbot_open_ai_streaming":
2451
  chatbot_open_ai_streaming_visible = gr.update(visible=True)
2452
+ else:
2453
  chatbot_jutor_visible = gr.update(visible=True)
2454
+ ai_name_update = gr.update(value=chatbot_name)
2455
 
2456
+ return chatbot_select_accordion_visible, all_chatbot_select_btn_visible, chatbot_open_ai_visible, chatbot_open_ai_streaming_visible, chatbot_jutor_visible, ai_name_update
2457
+
2458
+ def update_avatar_images(avatar_images, maimai_chatbot_description_value):
2459
+ value = [[
2460
+ "請問你是誰?",
2461
+ maimai_chatbot_description_value
2462
+ ]]
2463
+ ai_chatbot_update = gr.update(avatar_images=avatar_images, value=value)
2464
+ return ai_chatbot_update
2465
+
2466
+ def show_all_chatbot_accordion():
2467
+ chatbot_select_accordion_visible = gr.update(visible=True)
2468
+ all_chatbot_select_btn_visible = gr.update(visible=False)
2469
+ return chatbot_select_accordion_visible, all_chatbot_select_btn_visible
2470
 
2471
  # --- Slide mode ---
2472
  def update_slide(direction):
 
2633
  }
2634
  }
2635
  </script>
2636
+
2637
+ <script>
2638
+ var selectButtons = document.querySelectorAll('.chatbot_select_btn');
2639
+
2640
+ // 为每个按钮添加点击事件监听器
2641
+ selectButtons.forEach(function(button) {
2642
+ button.addEventListener('click', function() {
2643
+ // 获取 #chatbot_select_accordion 下的第一个 button 元素
2644
+ var firstButton = document.querySelector('#chatbot_select_accordion button');
2645
+ var displayDiv = document.querySelector('#chatbot_select_accordion div:nth-child(3)');
2646
+ // 检查这个按钮是否存在
2647
+ if (firstButton) {
2648
+ // 移除 'open' 类
2649
+ firstButton.classList.remove('open');
2650
+ }
2651
+ if (displayDiv) {
2652
+ // display none
2653
+ displayDiv.style.display = 'none';
2654
+ }
2655
+ });
2656
+ });
2657
+ </script>
2658
  """
2659
 
2660
  with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, secondary_hue=gr.themes.colors.amber, text_size = gr.themes.sizes.text_lg), head=HEAD) as demo:
 
2673
  key_moments_state = gr.State() # 使用 gr.State 存储 key_moments
2674
  streaming_chat_thread_id_state = gr.State() # 使用 gr.State 存储 streaming_chat_thread_id
2675
  with gr.Tab("AI小精靈"):
2676
+ with gr.Row():
2677
+ all_chatbot_select_btn = gr.Button("選擇 AI 小精靈 👈", elem_id="all_chatbot_select_btn", visible=False, variant="secondary", size="sm")
2678
+ with gr.Accordion("選擇 AI 小精靈", elem_id="chatbot_select_accordion") as chatbot_select_accordion:
2679
  with gr.Row():
2680
+ user_avatar = "https://em-content.zobj.net/source/google/263/flushed-face_1f633.png"
2681
+ ai_chatbot_bot_avatar = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
2682
  with gr.Column(scale=1, variant="panel", visible=False):
2683
  chatbot_avatar_url = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
2684
  chatbot_description = """Hi,我是你的AI學伴【飛特精靈】,\n
 
2706
  chatbot_open_ai_streaming_select_btn = gr.Button("👆選擇【飛特音速】", elem_id="streaming_chatbot_btn", visible=True, variant="primary")
2707
  gr.Markdown(value=streaming_chatbot_description, visible=True)
2708
  with gr.Column(scale=1, variant="panel"):
2709
+ 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"
2710
+ foxcat_avatar_images = gr.State([user_avatar, foxcat_chatbot_avatar_url])
2711
+ foxcat_chatbot_description = """Hi,我是【狐狸貓】,\n
2712
  也可以陪你一起學習本次的內容,有什麼問題都可以問我喔!\n
2713
  🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!\n
2714
  🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!\n
2715
  🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!\n
2716
+ 💤 精靈們體力都有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!
2717
+ """
2718
+ foxcat_chatbot_name = gr.State("foxcat")
2719
+ gr.Image(value=foxcat_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
2720
+ foxcat_chatbot_select_btn = gr.Button("👆選擇【狐狸貓】", visible=True, variant="primary", elem_classes="chatbot_select_btn")
2721
+ foxcat_chatbot_description_value = gr.Markdown(value=foxcat_chatbot_description, visible=True)
2722
+ # 梨梨
2723
+ with gr.Column(scale=1, variant="panel"):
2724
+ lili_chatbot_avatar_url = "https://junyitopicimg.s3.amazonaws.com/live/v1283-new-topic-44-icon.png?v=20230529071206714"
2725
+ lili_avatar_images = gr.State([user_avatar, lili_chatbot_avatar_url])
2726
+ lili_chatbot_description = """你好,我是溫柔的【梨梨】, \n
2727
+ 很高興可以在這裡陪伴你學習。如果你有任何疑問,請隨時向我提出哦! \n
2728
+ 🤔 如果你在思考如何提問,可以嘗試點擊下方的「問題一」、「問題二」或「問題三」,我會為你生成一些問題來幫助你啟動思考。 \n
2729
+ 🗣️ 你也可以使用右下角的語音輸入功能,讓我幫你將語音轉化為文字,這樣可以更加方便快捷。\n
2730
+ 🔠 當然,你也可以直接通過鍵盤輸入你的問題,我將盡我所能為你提供答案。\n
2731
+ 💤 請理解,即使是我們這些精靈,也有疲憊的時候,每次學習後我能回答的問題有限。如果達到上限,讓我稍作休息之後再繼續回答你的問題吧!
2732
  """
2733
+ lili_chatbot_name = gr.State("lili")
2734
+ gr.Image(value=lili_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
2735
+ lili_chatbot_select_btn = gr.Button("👆選擇【梨梨】", visible=True, variant="primary", elem_classes="chatbot_select_btn")
2736
+ lili_chatbot_description_value = gr.Markdown(value=lili_chatbot_description, visible=True)
2737
+ # 麥麥
2738
+ with gr.Column(scale=1, variant="panel"):
2739
+ 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"
2740
+ maimai_avatar_images = gr.State([user_avatar, maimai_chatbot_avatar_url])
2741
+ maimai_chatbot_description = """Hi,我是迷人的【麥麥】,\n
2742
+ 我在這裡等著和你一起探索新知,任何疑問都可以向我提出!\n
2743
+ 🤔 如果你不知道從���裡開始,試試左下方的「問題一」、「問題二」、「問題三」,我會為你提供一些啟發思考的問題。\n
2744
+ 🗣️ 你也可以利用右下角的語音輸入功能,讓我將你的語音轉成文字,是不是很酷?\n
2745
+ 🔠 當然,你也可以直接透過鍵盤向我發問,我會全力以赴來回答你的每一個問題。\n
2746
+ 💤 我們這些精靈也需要休息,每次學習我們只能回答十個問題,當達到上限時,請給我一點時間充電再繼續。
2747
+ """
2748
+ maimai_chatbot_name = gr.State("maimai")
2749
+ gr.Image(value=maimai_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
2750
+ maimai_chatbot_select_btn = gr.Button("👆選擇【麥麥】", visible=True, variant="primary", elem_classes="chatbot_select_btn")
2751
+ maimai_chatbot_description_value = gr.Markdown(value=maimai_chatbot_description, visible=True)
2752
 
2753
  with gr.Row("飛特精靈") as chatbot_open_ai:
2754
  with gr.Column():
 
2755
  bot_avatar = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
2756
  latex_delimiters = [{"left": "$", "right": "$", "display": False}]
2757
  chatbot_greeting = [[
 
2810
  💤 精靈們體力都有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!
2811
  """,
2812
  ]]
2813
+ ai_name = gr.Dropdown(
2814
+ label="選擇 AI 助理",
2815
+ choices=[
2816
+ ("梨梨","lili"),
2817
+ ("麥麥","maimai"),
2818
+ ("狐狸貓","foxcat")
2819
+ ],
2820
+ value="foxcat",
2821
+ visible=False
2822
  )
2823
+ ai_chatbot = gr.Chatbot(label="ai_chatbot", show_share_button=False, likeable=True, show_label=False, latex_delimiters=latex_delimiters, value=ai_chatbot_greeting)
2824
  ai_chatbot_socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
2825
  with gr.Row():
2826
  with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion_2:
 
2854
  with gr.Row():
2855
  worksheet_content_type_name = gr.Textbox(value="worksheet", visible=False)
2856
  worksheet_algorithm = gr.Dropdown(label="選擇教學策略或理論", choices=["Bloom認知階層理論", "Polya數學解題法", "CRA教學法"], value="Bloom認知階層理論", visible=False)
2857
+ worksheet_content_btn = gr.Button("生成學習單 📄", variant="primary", visible=True)
2858
  with gr.Accordion("微調", open=False):
2859
  worksheet_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2860
  worksheet_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
 
2875
  with gr.Row():
2876
  lesson_plan_content_type_name = gr.Textbox(value="lesson_plan", visible=False)
2877
  lesson_plan_time = gr.Slider(label="選擇課程時間(分鐘)", minimum=10, maximum=120, step=5, value=40)
2878
+ lesson_plan_btn = gr.Button("生成教案 📕", variant="primary", visible=True)
2879
  with gr.Accordion("微調", open=False):
2880
  lesson_plan_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2881
  lesson_plan_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
 
2896
  with gr.Row():
2897
  exit_ticket_content_type_name = gr.Textbox(value="exit_ticket", visible=False)
2898
  exit_ticket_time = gr.Slider(label="選擇出場券時間(分鐘)", minimum=5, maximum=10, step=1, value=8)
2899
+ exit_ticket_btn = gr.Button("生成出場券 🎟️", variant="primary", visible=True)
2900
  with gr.Accordion("微調", open=False):
2901
  exit_ticket_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
2902
  exit_ticket_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
 
3015
  mind_map_html = gr.HTML()
3016
 
3017
  # --- Event ---
3018
+ chatbot_select_outputs=[chatbot_select_accordion, all_chatbot_select_btn, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor, ai_name]
3019
+
3020
+ # OPEN AI CHATBOT SELECT
3021
  chatbot_open_ai_select_btn.click(
3022
  chatbot_select,
3023
  inputs=[chatbot_open_ai_name],
3024
+ outputs=chatbot_select_outputs
3025
  )
3026
  chatbot_open_ai_streaming_select_btn.click(
3027
  chatbot_select,
3028
  inputs=[chatbot_open_ai_streaming_name],
3029
+ outputs=chatbot_select_outputs
3030
  ).then(
3031
  create_thread_id,
3032
  inputs=[],
3033
  outputs=[streaming_chat_thread_id_state]
3034
  )
3035
+ foxcat_chatbot_select_btn.click(
3036
+ chatbot_select,
3037
+ inputs=[foxcat_chatbot_name],
3038
+ outputs=chatbot_select_outputs
3039
+ ).then(
3040
+ update_avatar_images,
3041
+ inputs=[foxcat_avatar_images, foxcat_chatbot_description_value],
3042
+ outputs=[ai_chatbot],
3043
+ scroll_to_output=True
3044
+ )
3045
+ lili_chatbot_select_btn.click(
3046
  chatbot_select,
3047
+ inputs=[lili_chatbot_name],
3048
+ outputs=chatbot_select_outputs
3049
+ ).then(
3050
+ update_avatar_images,
3051
+ inputs=[lili_avatar_images, lili_chatbot_description_value],
3052
+ outputs=[ai_chatbot],
3053
+ scroll_to_output=True
3054
+ )
3055
+ maimai_chatbot_select_btn.click(
3056
+ chatbot_select,
3057
+ inputs=[maimai_chatbot_name],
3058
+ outputs=chatbot_select_outputs
3059
+ ).then(
3060
+ update_avatar_images,
3061
+ inputs=[maimai_avatar_images, maimai_chatbot_description_value],
3062
+ outputs=[ai_chatbot],
3063
+ scroll_to_output=True
3064
+ )
3065
+ # ALL CHATBOT SELECT LIST
3066
+ all_chatbot_select_btn.click(
3067
+ show_all_chatbot_accordion,
3068
+ inputs=[],
3069
+ outputs=[chatbot_select_accordion, all_chatbot_select_btn]
3070
  )
3071
 
3072
  # OPENAI ASSISTANT CHATBOT 模式
 
3082
  outputs=[msg]
3083
  )
3084
  # OPENAI ASSISTANT CHATBOT 連接按鈕點擊事件
3085
+ def setup_question_button_click(button, inputs_list, outputs_list, chat_func, scroll_to_output=True):
3086
+ button.click(
3087
+ chat_func,
3088
+ inputs=inputs_list,
3089
+ outputs=outputs_list,
3090
+ scroll_to_output=scroll_to_output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3091
  )
3092
+ question_buttons = [btn_1, btn_2, btn_3]
3093
+ for question_btn in question_buttons:
3094
+ inputs_list = [password, video_id, user_data, thread_id, trascript_state, key_moments, question_btn, chatbot, content_subject, content_grade, questions_answers_json, ai_chatbot_socratic_mode_btn]
3095
+ outputs_list = [msg, chatbot, thread_id]
3096
+ setup_question_button_click(question_btn, inputs_list, outputs_list, chat_with_opan_ai_assistant)
3097
+
3098
+ # 為生成問題按鈕設定特殊的點擊事件
3099
  btn_create_question.click(
3100
  change_questions,
3101
+ inputs=[password, df_string_output],
3102
+ outputs=question_buttons
3103
  )
3104
 
3105
  # 其他精靈 ai_chatbot 模式
 
3110
  scroll_to_output=True
3111
  )
3112
  # 其他精靈 ai_chatbot 连接按钮点击事件
3113
+ ai_chatbot_buttons = [ai_chatbot_question_1, ai_chatbot_question_2, ai_chatbot_question_3]
3114
+ for ai_question_btn in ai_chatbot_buttons:
3115
+ inputs_list = [ai_name, password, video_id, user_data, trascript_state, key_moments, ai_question_btn, ai_chatbot, content_subject, content_grade, questions_answers_json, ai_chatbot_socratic_mode_btn]
3116
+ outputs_list = [ai_msg, ai_chatbot]
3117
+ setup_question_button_click(ai_question_btn, inputs_list, outputs_list, chat_with_ai)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3118
 
3119
  # file_upload.change(process_file, inputs=file_upload, outputs=df_string_output)
3120
  # file_upload.change(process_file, inputs=file_upload, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
 
3180
  inputs=update_state_inputs,
3181
  outputs=update_state_outputs
3182
  )
 
3183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3184
 
3185
+ # --- CRUD admin ---
3186
+ def setup_content_buttons(buttons_config):
3187
+ for config in buttons_config:
3188
+ button = config['button']
3189
+ action = config['action']
3190
+ inputs = config['inputs']
3191
+ outputs = config['outputs']
3192
+ button.click(
3193
+ fn=action,
3194
+ inputs=inputs,
3195
+ outputs=outputs
3196
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3197
 
3198
+ content_buttons_config = [
3199
+ # Transcript actions
3200
+ {
3201
+ 'button': transcript_get_button,
3202
+ 'action': get_LLM_content,
3203
+ 'inputs': [video_id, transcript_kind],
3204
+ 'outputs': [df_string_output]
3205
+ },
3206
+ {
3207
+ 'button': transcript_create_button,
3208
+ 'action': create_LLM_content,
3209
+ 'inputs': [video_id, df_string_output, transcript_kind],
3210
+ 'outputs': [df_string_output]
3211
+ },
3212
+ {
3213
+ 'button': transcript_delete_button,
3214
+ 'action': delete_LLM_content,
3215
+ 'inputs': [video_id, transcript_kind],
3216
+ 'outputs': [df_string_output]
3217
+ },
3218
+ {
3219
+ 'button': transcript_edit_button,
3220
+ 'action': enable_edit_mode,
3221
+ 'inputs': [],
3222
+ 'outputs': [df_string_output]
3223
+ },
3224
+ {
3225
+ 'button': transcript_update_button,
3226
+ 'action': update_LLM_content,
3227
+ 'inputs': [video_id, df_string_output, transcript_kind],
3228
+ 'outputs': [df_string_output]
3229
+ },
3230
+ # Reading passage actions
3231
+ {
3232
+ 'button': reading_passage_get_button,
3233
+ 'action': get_LLM_content,
3234
+ 'inputs': [video_id, reading_passage_kind],
3235
+ 'outputs': [reading_passage_text]
3236
+ },
3237
+ {
3238
+ 'button': reading_passage_create_button,
3239
+ 'action': create_LLM_content,
3240
+ 'inputs': [video_id, df_string_output, reading_passage_kind],
3241
+ 'outputs': [reading_passage_text]
3242
+ },
3243
+ {
3244
+ 'button': reading_passage_delete_button,
3245
+ 'action': delete_LLM_content,
3246
+ 'inputs': [video_id, reading_passage_kind],
3247
+ 'outputs': [reading_passage_text]
3248
+ },
3249
+ {
3250
+ 'button': reading_passage_edit_button,
3251
+ 'action': enable_edit_mode,
3252
+ 'inputs': [],
3253
+ 'outputs': [reading_passage_text]
3254
+ },
3255
+ {
3256
+ 'button': reading_passage_update_button,
3257
+ 'action': update_LLM_content,
3258
+ 'inputs': [video_id, reading_passage_text, reading_passage_kind],
3259
+ 'outputs': [reading_passage_text]
3260
+ },
3261
+ # Summary actions
3262
+ {
3263
+ 'button': summary_get_button,
3264
+ 'action': get_LLM_content,
3265
+ 'inputs': [video_id, summary_kind],
3266
+ 'outputs': [summary_text]
3267
+ },
3268
+ {
3269
+ 'button': summary_create_button,
3270
+ 'action': create_LLM_content,
3271
+ 'inputs': [video_id, df_string_output, summary_kind],
3272
+ 'outputs': [summary_text]
3273
+ },
3274
+ {
3275
+ 'button': summary_delete_button,
3276
+ 'action': delete_LLM_content,
3277
+ 'inputs': [video_id, summary_kind],
3278
+ 'outputs': [summary_text]
3279
+ },
3280
+ {
3281
+ 'button': summary_edit_button,
3282
+ 'action': enable_edit_mode,
3283
+ 'inputs': [],
3284
+ 'outputs': [summary_text]
3285
+ },
3286
+ {
3287
+ 'button': summary_update_button,
3288
+ 'action': update_LLM_content,
3289
+ 'inputs': [video_id, summary_text, summary_kind],
3290
+ 'outputs': [summary_text]
3291
+ },
3292
+ # Key moments actions
3293
+ {
3294
+ 'button': key_moments_get_button,
3295
+ 'action': get_LLM_content,
3296
+ 'inputs': [video_id, key_moments_kind],
3297
+ 'outputs': [key_moments]
3298
+ },
3299
+ {
3300
+ 'button': key_moments_create_button,
3301
+ 'action': create_LLM_content,
3302
+ 'inputs': [video_id, df_string_output, key_moments_kind],
3303
+ 'outputs': [key_moments]
3304
+ },
3305
+ {
3306
+ 'button': key_moments_delete_button,
3307
+ 'action': delete_LLM_content,
3308
+ 'inputs': [video_id, key_moments_kind],
3309
+ 'outputs': [key_moments]
3310
+ },
3311
+ {
3312
+ 'button': key_moments_edit_button,
3313
+ 'action': enable_edit_mode,
3314
+ 'inputs': [],
3315
+ 'outputs': [key_moments]
3316
+ },
3317
+ {
3318
+ 'button': key_moments_update_button,
3319
+ 'action': update_LLM_content,
3320
+ 'inputs': [video_id, key_moments, key_moments_kind],
3321
+ 'outputs': [key_moments]
3322
+ },
3323
+ # Questions actions
3324
+ {
3325
+ 'button': questions_get_button,
3326
+ 'action': get_LLM_content,
3327
+ 'inputs': [video_id, questions_kind],
3328
+ 'outputs': [questions_json]
3329
+ },
3330
+ {
3331
+ 'button': questions_create_button,
3332
+ 'action': create_LLM_content,
3333
+ 'inputs': [video_id, df_string_output, questions_kind],
3334
+ 'outputs': [questions_json]
3335
+ },
3336
+ {
3337
+ 'button': questions_delete_button,
3338
+ 'action': delete_LLM_content,
3339
+ 'inputs': [video_id, questions_kind],
3340
+ 'outputs': [questions_json]
3341
+ },
3342
+ {
3343
+ 'button': questions_edit_button,
3344
+ 'action': enable_edit_mode,
3345
+ 'inputs': [],
3346
+ 'outputs': [questions_json]
3347
+ },
3348
+ {
3349
+ 'button': questions_update_button,
3350
+ 'action': update_LLM_content,
3351
+ 'inputs': [video_id, questions_json, questions_kind],
3352
+ 'outputs': [questions_json]
3353
+ },
3354
+ # Questions answers actions
3355
+ {
3356
+ 'button': questions_answers_get_button,
3357
+ 'action': get_LLM_content,
3358
+ 'inputs': [video_id, questions_answers_kind],
3359
+ 'outputs': [questions_answers_json]
3360
+ },
3361
+ {
3362
+ 'button': questions_answers_create_button,
3363
+ 'action': create_LLM_content,
3364
+ 'inputs': [video_id, df_string_output, questions_answers_kind],
3365
+ 'outputs': [questions_answers_json]
3366
+ },
3367
+ {
3368
+ 'button': questions_answers_delete_button,
3369
+ 'action': delete_LLM_content,
3370
+ 'inputs': [video_id, questions_answers_kind],
3371
+ 'outputs': [questions_answers_json]
3372
+ },
3373
+ {
3374
+ 'button': questions_answers_edit_button,
3375
+ 'action': enable_edit_mode,
3376
+ 'inputs': [],
3377
+ 'outputs': [questions_answers_json]
3378
+ },
3379
+ {
3380
+ 'button': questions_answers_update_button,
3381
+ 'action': update_LLM_content,
3382
+ 'inputs': [video_id, questions_answers_json, questions_answers_kind],
3383
+ 'outputs': [questions_answers_json]
3384
+ },
3385
+ ]
3386
+ setup_content_buttons(content_buttons_config)
3387
+
3388
+ # --- Education Material ---
3389
+ def setup_education_buttons(buttons_config):
3390
+ for config in buttons_config:
3391
+ button = config["button"]
3392
+ action = config["action"]
3393
+ inputs = config["inputs"]
3394
+ outputs = config["outputs"]
3395
+ button.click(
3396
+ fn=action,
3397
+ inputs=inputs,
3398
+ outputs=outputs
3399
+ )
3400
+ education_buttons_config = [
3401
+ # 學習單相關按鈕
3402
+ {
3403
+ "button": worksheet_content_btn,
3404
+ "action": get_ai_content,
3405
+ "inputs": [password, video_id, df_string_output, content_subject, content_grade, content_level, worksheet_algorithm, worksheet_content_type_name],
3406
+ "outputs": [worksheet_exam_result_original, worksheet_exam_result, worksheet_prompt, worksheet_exam_result_prompt]
3407
+ },
3408
+ {
3409
+ "button": worksheet_exam_result_fine_tune_btn,
3410
+ "action": generate_exam_fine_tune_result,
3411
+ "inputs": [password, worksheet_exam_result_prompt, df_string_output, worksheet_exam_result, worksheet_exam_result_fine_tune_prompt],
3412
+ "outputs": [worksheet_exam_result]
3413
+ },
3414
+ {
3415
+ "button": worksheet_download_exam_result_button,
3416
+ "action": download_exam_result,
3417
+ "inputs": [worksheet_exam_result],
3418
+ "outputs": [worksheet_exam_result_word_link]
3419
+ },
3420
+ {
3421
+ "button": worksheet_exam_result_retrun_original,
3422
+ "action": return_original_exam_result,
3423
+ "inputs": [worksheet_exam_result_original],
3424
+ "outputs": [worksheet_exam_result]
3425
+ },
3426
+ # 教案相關按鈕
3427
+ {
3428
+ "button": lesson_plan_btn,
3429
+ "action": get_ai_content,
3430
+ "inputs": [password, video_id, df_string_output, content_subject, content_grade, content_level, lesson_plan_time, lesson_plan_content_type_name],
3431
+ "outputs": [lesson_plan_exam_result_original, lesson_plan_exam_result, lesson_plan_prompt, lesson_plan_exam_result_prompt]
3432
+ },
3433
+ {
3434
+ "button": lesson_plan_exam_result_fine_tune_btn,
3435
+ "action": generate_exam_fine_tune_result,
3436
+ "inputs": [password, lesson_plan_exam_result_prompt, df_string_output, lesson_plan_exam_result, lesson_plan_exam_result_fine_tune_prompt],
3437
+ "outputs": [lesson_plan_exam_result]
3438
+ },
3439
+ {
3440
+ "button": lesson_plan_download_exam_result_button,
3441
+ "action": download_exam_result,
3442
+ "inputs": [lesson_plan_exam_result],
3443
+ "outputs": [lesson_plan_exam_result_word_link]
3444
+ },
3445
+ {
3446
+ "button": lesson_plan_exam_result_retrun_original,
3447
+ "action": return_original_exam_result,
3448
+ "inputs": [lesson_plan_exam_result_original],
3449
+ "outputs": [lesson_plan_exam_result]
3450
+ },
3451
+ # 出場券相關按鈕
3452
+ {
3453
+ "button": exit_ticket_btn,
3454
+ "action": get_ai_content,
3455
+ "inputs": [password, video_id, df_string_output, content_subject, content_grade, content_level, exit_ticket_time, exit_ticket_content_type_name],
3456
+ "outputs": [exit_ticket_exam_result_original, exit_ticket_exam_result, exit_ticket_prompt, exit_ticket_exam_result_prompt]
3457
+ },
3458
+ {
3459
+ "button": exit_ticket_exam_result_fine_tune_btn,
3460
+ "action": generate_exam_fine_tune_result,
3461
+ "inputs": [password, exit_ticket_exam_result_prompt, df_string_output, exit_ticket_exam_result, exit_ticket_exam_result_fine_tune_prompt],
3462
+ "outputs": [exit_ticket_exam_result]
3463
+ },
3464
+ {
3465
+ "button": exit_ticket_download_exam_result_button,
3466
+ "action": download_exam_result,
3467
+ "inputs": [exit_ticket_exam_result],
3468
+ "outputs": [exit_ticket_exam_result_word_link]
3469
+ },
3470
+ {
3471
+ "button": exit_ticket_exam_result_retrun_original,
3472
+ "action": return_original_exam_result,
3473
+ "inputs": [exit_ticket_exam_result_original],
3474
+ "outputs": [exit_ticket_exam_result]
3475
+ }
3476
+ ]
3477
+ setup_education_buttons(education_buttons_config)
3478
 
3479
  # init_params
3480
  init_outputs = [
chatbot.py CHANGED
@@ -39,10 +39,11 @@ class Chatbot:
39
  return key_moments_text
40
 
41
 
42
- def chat(self, user_message, chat_history, socratic_mode=False, service_type='jutor'):
43
  messages = self.prepare_messages(chat_history, user_message)
44
  system_prompt = self.instructions
45
- if service_type in ['jutor', 'groq', 'claude3']:
 
46
  response_text = self.chat_with_service(service_type, system_prompt, messages)
47
  return response_text
48
  else:
@@ -66,10 +67,12 @@ class Chatbot:
66
  return messages
67
 
68
  def chat_with_service(self, service_type, system_prompt, messages):
69
- if service_type == 'jutor':
70
  return self.chat_with_jutor(system_prompt, messages)
71
- elif service_type == 'groq':
72
- return self.chat_with_groq(system_prompt, messages)
 
 
73
  elif service_type == 'claude3':
74
  return self.chat_with_claude3(system_prompt, messages)
75
  else:
@@ -83,6 +86,8 @@ class Chatbot:
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": {
@@ -99,11 +104,19 @@ class Chatbot:
99
  response_completion = response_data['data']['choices'][0]['message']['content'].strip()
100
  return response_completion
101
 
102
- def chat_with_groq(self, system_prompt, messages):
103
  # system_prompt insert to messages 的最前面 {"role": "system", "content": system_prompt}
104
  messages.insert(0, {"role": "system", "content": system_prompt})
 
 
 
 
 
 
 
 
105
  request_payload = {
106
- "model": "mixtral-8x7b-32768",
107
  "messages": messages,
108
  "max_tokens": 500 # 設定一個較大的值,可根據需要調整
109
  }
@@ -118,6 +131,8 @@ class Chatbot:
118
 
119
  model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
120
  # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
 
 
121
  kwargs = {
122
  "modelId": model_id,
123
  "contentType": "application/json",
@@ -129,7 +144,6 @@ class Chatbot:
129
  "messages": messages
130
  })
131
  }
132
- print(messages)
133
  # 建立 message API,讀取回應
134
  bedrock_client = self.ai_client
135
  response = bedrock_client.invoke_model(**kwargs)
 
39
  return key_moments_text
40
 
41
 
42
+ def chat(self, user_message, chat_history, socratic_mode=False, service_type='openai'):
43
  messages = self.prepare_messages(chat_history, user_message)
44
  system_prompt = self.instructions
45
+ service_type_list = ['openai', 'claude3', 'groq_llama3', 'groq_mixtral']
46
+ if service_type in service_type_list:
47
  response_text = self.chat_with_service(service_type, system_prompt, messages)
48
  return response_text
49
  else:
 
67
  return messages
68
 
69
  def chat_with_service(self, service_type, system_prompt, messages):
70
+ if service_type == 'openai':
71
  return self.chat_with_jutor(system_prompt, messages)
72
+ elif service_type == 'groq_llama3':
73
+ return self.chat_with_groq(service_type, system_prompt, messages)
74
+ elif service_type == 'groq_mixtral':
75
+ return self.chat_with_groq(service_type, system_prompt, messages)
76
  elif service_type == 'claude3':
77
  return self.chat_with_claude3(system_prompt, messages)
78
  else:
 
86
  "x-api-key": self.jutor_chat_key,
87
  }
88
  model = "gpt-4-turbo"
89
+ print("======model======")
90
+ print(model)
91
  # model = "gpt-3.5-turbo-0125"
92
  data = {
93
  "data": {
 
104
  response_completion = response_data['data']['choices'][0]['message']['content'].strip()
105
  return response_completion
106
 
107
+ def chat_with_groq(self, model_name, system_prompt, messages):
108
  # system_prompt insert to messages 的最前面 {"role": "system", "content": system_prompt}
109
  messages.insert(0, {"role": "system", "content": system_prompt})
110
+ model_name_dict = {
111
+ "groq_llama3": "llama3-70b-8192",
112
+ "groq_mixtral": "mixtral-8x7b-32768"
113
+ }
114
+ model = model_name_dict.get(model_name)
115
+ print("======model======")
116
+ print(model)
117
+
118
  request_payload = {
119
+ "model": model,
120
  "messages": messages,
121
  "max_tokens": 500 # 設定一個較大的值,可根據需要調整
122
  }
 
131
 
132
  model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
133
  # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
134
+ print("======model_id======")
135
+ print(model_id)
136
  kwargs = {
137
  "modelId": model_id,
138
  "contentType": "application/json",
 
144
  "messages": messages
145
  })
146
  }
 
147
  # 建立 message API,讀取回應
148
  bedrock_client = self.ai_client
149
  response = bedrock_client.invoke_model(**kwargs)
educational_material.py CHANGED
@@ -32,6 +32,115 @@ class EducationalMaterial:
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 = ""
@@ -420,67 +529,6 @@ class EducationalMaterial:
420
  """
421
  return exit_ticket_prompt
422
 
423
- def create_ai_content(self, ai_client, request_payload):
424
- user_content = self.build_user_content()
425
- messages = self.build_messages(user_content)
426
- request_payload['messages'] = messages
427
- response_content = self.send_ai_request(ai_client, request_payload)
428
-
429
- return response_content
430
-
431
- def build_user_content(self):
432
- if self.content_type == 'worksheet':
433
- specific_feature_text = f"理論模型: {self.specific_feature}"
434
- elif self.content_type == 'lesson_plan':
435
- specific_feature_text = f"時間: {self.specific_feature} 分鐘"
436
- elif self.content_type == 'exit_ticket':
437
- specific_feature_text = f"時間: {self.specific_feature} 分鐘"
438
-
439
- # 根据属性构建用户内容
440
- user_content = f"""
441
- 課程脈絡 or 逐字稿:{self.context}
442
- 主題:{self.topic}
443
- 年級:{self.grade}
444
- 難度:{self.level}
445
- {specific_feature_text}
446
-
447
- 請根據逐字稿進行以下工作:
448
- - 不要提到 【逐字稿】 這個詞,直接給出內容即可
449
- - 遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。
450
- - 如果是中文素材,請嚴格使用 zh-TW
451
- - 請用 {self.grade} 年級的口吻,不要用太難的詞彙
452
- - {self.generate_content_prompt()}
453
- """
454
- print("====User content====")
455
- print(user_content)
456
- print("====User content====")
457
- return user_content
458
-
459
- def build_messages(self, user_content):
460
- messages = [{"role": "system", "content": self.system_content},
461
- {"role": "user", "content": user_content}]
462
- return messages
463
 
464
- def send_ai_request(self, ai_client, request_payload):
465
- try:
466
- response = ai_client.chat.completions.create(**request_payload)
467
- response_content = response.choices[0].message.content.strip()
468
- return response_content
469
- except Exception as e:
470
- print(f"An error occurred: {e}")
471
- return "Error generating content."
472
 
473
- def build_fine_tune_user_content(self, original_prompt, result, fine_tune_prompt):
474
- user_content = f"""
475
- 這是逐字稿:{self.context}
476
- ---
477
- 這是預設的 prompt
478
- {original_prompt}
479
- ---
480
- 產生了以下的結果:
481
- {result}
482
- ---
483
- 但我不是很滿意,請根據以下的調整,產生新的結果
484
- {fine_tune_prompt}
485
- """
486
- return user_content
 
32
  self.content_type = content_type # 'worksheet' or 'lesson_plan'
33
  self.system_content = "你是一個擅長資料分析跟影片教學備課的老師,請精讀資料文本,自行判斷資料的種類,使用 zh-TW,遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。"
34
 
35
+ def get_ai_content(self, AI_Client ,ai_type="openai"):
36
+ system_content = self.system_content
37
+ user_content = self._build_user_content()
38
+ if ai_type.lower() == "openai":
39
+ return self.send_openai_request(AI_Client, system_content, user_content)
40
+ elif ai_type.lower() == "bedrock":
41
+ return self.send_bedrock_request(AI_Client, system_content, user_content)
42
+ else:
43
+ raise ValueError("Unsupported AI type. Please choose 'openai' or 'redrock'.")
44
+
45
+ def _build_user_content(self):
46
+ if self.content_type == 'worksheet':
47
+ specific_feature_text = f"理論模型: {self.specific_feature}"
48
+ elif self.content_type == 'lesson_plan':
49
+ specific_feature_text = f"時間: {self.specific_feature} 分鐘"
50
+ elif self.content_type == 'exit_ticket':
51
+ specific_feature_text = f"時間: {self.specific_feature} 分鐘"
52
+
53
+ # 根据属性构建用户内容
54
+ user_content = f"""
55
+ 課程脈絡 or 逐字稿:{self.context}
56
+ 主題:{self.topic}
57
+ 年級:{self.grade}
58
+ 難度:{self.level}
59
+ {specific_feature_text}
60
+
61
+ 請根據逐字稿進行以下工作:
62
+ - 不要提到 【逐字稿】 這個詞,直接給出內容即可
63
+ - 遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。
64
+ - 如果是中文素材,請嚴格使用 zh-TW
65
+ - 請用 {self.grade} 年級的口吻,不要用太難的詞彙
66
+ - {self.generate_content_prompt()}
67
+ """
68
+ print("====User content====")
69
+ print(user_content)
70
+ print("====User content====")
71
+ return user_content
72
+
73
+ def get_fine_tuned_ai_content(self, ai_client, ai_type, original_prompt, result, fine_tune_prompt):
74
+ system_content = self.system_content
75
+ user_content = self._build_fine_tune_user_content(original_prompt, result, fine_tune_prompt)
76
+ if ai_type.lower() == "openai":
77
+ return self.send_openai_request(ai_client, system_content, user_content)
78
+ elif ai_type.lower() == "bedrock":
79
+ return self.send_bedrock_request(ai_client, system_content, user_content)
80
+ else:
81
+ raise ValueError("Unsupported AI type. Please choose 'openai' or 'redrock'.")
82
+
83
+ def _build_fine_tune_user_content(self, original_prompt, result, fine_tune_prompt):
84
+ user_content = f"""
85
+ 這是逐字稿:{self.context}
86
+ ---
87
+ 這是預設的 prompt
88
+ {original_prompt}
89
+ ---
90
+ 產生了以下的結果:
91
+ {result}
92
+ ---
93
+ 但我不是很滿意,請根據以下的調整,產生新的結果
94
+ {fine_tune_prompt}
95
+ """
96
+ return user_content
97
+
98
+ def send_openai_request(self, AI_Client, system_content, user_content):
99
+ OPEN_AI_CLIENT = AI_Client
100
+ messages = [{"role": "system", "content": system_content}, {"role": "user", "content": user_content}]
101
+ request_payload = {
102
+ "model": "gpt-4-turbo",
103
+ "messages": messages,
104
+ "max_tokens": 512,
105
+ "temperature": 0.9,
106
+ "stream": False,
107
+ }
108
+ try:
109
+ response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
110
+ return response.choices[0].message.content.strip()
111
+ except Exception as e:
112
+ print(f"OpenAI failed: {e}")
113
+ raise # Optionally re-raise the exception if fallback is not desired
114
+
115
+ def send_bedrock_request(self, AI_Client, system_content, user_content):
116
+ BEDROCK_CLIENT = AI_Client
117
+ #REDROCK
118
+ messages = [
119
+ {"role": "user", "content": user_content}
120
+ ]
121
+ model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
122
+ # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
123
+ kwargs = {
124
+ "modelId": model_id,
125
+ "contentType": "application/json",
126
+ "accept": "application/json",
127
+ "body": json.dumps({
128
+ "anthropic_version": "bedrock-2023-05-31",
129
+ "max_tokens": 4000,
130
+ "system": system_content,
131
+ "messages": messages
132
+ })
133
+ }
134
+
135
+ try:
136
+ response = response = BEDROCK_CLIENT.invoke_model(**kwargs)
137
+ response_body = json.loads(response.get('body').read())
138
+ response_content = response_body.get('content')[0].get('text')
139
+ return response_content
140
+ except Exception as e:
141
+ print(f"Bedrock failed: {e}")
142
+ raise
143
+
144
  def _prepare_context(self, context):
145
  context_json = json.loads(context)
146
  processed_context = ""
 
529
  """
530
  return exit_ticket_prompt
531
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
 
 
 
 
 
 
 
 
 
533
 
534
+
 
 
 
 
 
 
 
 
 
 
 
 
 
storage_service.py CHANGED
@@ -24,6 +24,12 @@ class GoogleCloudStorage:
24
  blob.upload_from_string(content)
25
  print(f"String content uploaded to {destination_blob_name} in GCS.")
26
  return None
 
 
 
 
 
 
27
 
28
  def download_as_string(self, bucket_name, source_blob_name):
29
  blob = self.client.bucket(bucket_name).blob(source_blob_name)
@@ -42,3 +48,8 @@ class GoogleCloudStorage:
42
  self.upload_file(bucket_name, file_name, file_path)
43
  self.make_blob_public(bucket_name, file_name)
44
  return self.get_public_url(bucket_name, file_name)
 
 
 
 
 
 
24
  blob.upload_from_string(content)
25
  print(f"String content uploaded to {destination_blob_name} in GCS.")
26
  return None
27
+
28
+ def upload_json_string(self, bucket_name, destination_blob_name, json_data):
29
+ """Uploads a JSON string to a specified GCS bucket."""
30
+ blob = self.client.bucket(bucket_name).blob(destination_blob_name)
31
+ blob.upload_from_string(json_data, content_type='application/json')
32
+ print(f"JSON string uploaded to {destination_blob_name} in GCS.")
33
 
34
  def download_as_string(self, bucket_name, source_blob_name):
35
  blob = self.client.bucket(bucket_name).blob(source_blob_name)
 
48
  self.upload_file(bucket_name, file_name, file_path)
49
  self.make_blob_public(bucket_name, file_name)
50
  return self.get_public_url(bucket_name, file_name)
51
+
52
+ def delete_blob(self, bucket_name, blob_name):
53
+ blob = self.client.bucket(bucket_name).blob(blob_name)
54
+ blob.delete()
55
+ print(f"Blob {blob_name} deleted from {bucket_name}.")