youngtsai commited on
Commit
968db8f
·
1 Parent(s): 9ad2024

chat_with_ai

Browse files
Files changed (2) hide show
  1. app.py +45 -303
  2. chatbot.py +156 -0
app.py CHANGED
@@ -33,6 +33,8 @@ from storage_service import GoogleCloudStorage
33
 
34
  import boto3
35
 
 
 
36
  is_env_local = os.getenv("IS_ENV_LOCAL", "false") == "true"
37
  print(f"is_env_local: {is_env_local}")
38
 
@@ -1161,97 +1163,35 @@ def download_exam_result(content):
1161
  return word_path
1162
 
1163
  # ---- Chatbot ----
1164
- def chat_with_jutor(password, user_message, data, chat_history, socratic_mode=False):
1165
  verify_password(password)
1166
 
1167
- # 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
1168
  if chat_history is not None and len(chat_history) > 10:
1169
  error_msg = "此次對話超過上限"
1170
  raise gr.Error(error_msg)
1171
 
1172
- data_json = json.loads(data)
1173
- for entry in data_json:
1174
- entry.pop('embed_url', None) # Remove 'embed_url' if it exists
1175
- entry.pop('screenshot_path', None)
1176
-
1177
- if socratic_mode:
1178
- sys_content = f"""
1179
- 你是一個擅長資料分析跟影片教學的老師,user 為學生
1180
- 請用 {data} 為資料文本,自行判斷資料的種類,
1181
- 並進行對話,使用 台灣人的口與表達,及繁體中文zh-TW
1182
-
1183
- 如果是影片類型,不用解釋逐字稿格式,直接回答學生問題
1184
- 請你用蘇格拉底式的提問方式,引導學生思考,並且給予學生一些提示
1185
- 不要直接給予答案,讓學生自己思考
1186
- 但可以給予一些提示跟引導,例如給予影片的時間軸,讓學生自己去找答案
1187
-
1188
- 如果學生問了一些問題你無法判斷,請告訴學生你無法判斷,並建議學生可以問其他問題
1189
- 或者你可以問學生一些問題,幫助學生更好的理解資料
1190
-
1191
- 如果學生的問題與資料文本無關,請告訴學生你無法回答超出範圍的問題
1192
-
1193
- 最後,在你回答的開頭標註【蘇格拉底助教】
1194
- """
1195
- else:
1196
- sys_content = f"""
1197
- 你是一個擅長資料分析跟影片教學的老師,user 為學生
1198
- 請用 {data} 為資料文本,自行判斷資料的種類,
1199
- 並進行對話,使用 zh-TW
1200
-
1201
- 如果是影片類型,不用解釋逐字稿格式,直接回答學生問題
1202
- 但可以給予一些提示跟引導,例如給予影片的時間軸,讓學生可以找到相對應的時間點
1203
-
1204
- 如果學生問了一些問題你無法判斷,請告訴學生你無法判斷,並建議學生可以問其他問題
1205
- 或者你可以問學生一些問題,幫助學生更好的理解資料
1206
-
1207
- 如果學生的問題與資料文本無關,請告訴學生你無法回答超出範圍的問題
1208
- """
1209
-
1210
- messages = [
1211
- {"role": "system", "content": sys_content}
1212
- ]
1213
-
1214
- # if chat_history is not none, append role, content to messages
1215
- # chat_history = [(user, assistant), (user, assistant), ...]
1216
- # In the list, first one is user, then assistant
1217
- if chat_history is not None:
1218
- # 如果超過10則訊息,只保留最後10則訊息
1219
- if len(chat_history) > 10:
1220
- chat_history = chat_history[-10:]
1221
-
1222
- for chat in chat_history:
1223
- old_messages = [
1224
- {"role": "user", "content": chat[0]},
1225
- {"role": "assistant", "content": chat[1]}
1226
- ]
1227
- messages += old_messages
1228
- else:
1229
- pass
1230
-
1231
- messages.append({"role": "user", "content": user_message})
1232
- api_endpoint = "https://ci-live-feat-video-ai-dot-junyiacademy.appspot.com/api/v2/jutor/hf-chat"
1233
- headers = {
1234
- "Content-Type": "application/json",
1235
- "x-api-key": JUTOR_CHAT_KEY,
1236
- }
1237
- data = {
1238
- "data": {
1239
- "messages": messages,
1240
- "max_tokens": 512,
1241
- "temperature": 0.9,
1242
- "model": "gpt-4-1106-preview",
1243
- "stream": False,
1244
- }
1245
  }
 
 
1246
 
1247
- response = requests.post(api_endpoint, headers=headers, data=json.dumps(data))
1248
-
1249
- if response.status_code == 200:
1250
- # 处理响应数据
1251
- response_data = response.json()
1252
- prompt = response_data['data']['choices'][0]['message']['content'].strip()
1253
  # 更新聊天历史
1254
- new_chat_history = (user_message, prompt)
1255
  if chat_history is None:
1256
  chat_history = [new_chat_history]
1257
  else:
@@ -1259,102 +1199,11 @@ def chat_with_jutor(password, user_message, data, chat_history, socratic_mode=Fa
1259
 
1260
  # 返回聊天历史和空字符串清空输入框
1261
  return "", chat_history
1262
- else:
1263
  # 处理错误情况
1264
- print(f"Error: {response.status_code}")
1265
  return "请求失败,请稍后再试!", chat_history
1266
 
1267
- def chat_with_groq(password, user_message, data, chat_history, socratic_mode=False):
1268
- verify_password(password)
1269
-
1270
- # 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
1271
- if chat_history is not None and len(chat_history) > 10:
1272
- error_msg = "此次對話超過上限"
1273
- raise gr.Error(error_msg)
1274
-
1275
- print("=== 變數:user_message ===")
1276
- print(user_message)
1277
- print("=== 變數:chat_history ===")
1278
- print(chat_history)
1279
-
1280
- data_json = json.loads(data)
1281
- for entry in data_json:
1282
- entry.pop('embed_url', None) # Remove 'embed_url' if it exists
1283
- entry.pop('screenshot_path', None)
1284
-
1285
- if socratic_mode:
1286
- sys_content = f"""
1287
- 你是一個擅長資料分析跟影片教學的老師,user 為學生
1288
- 請用 {data} 為資料文本,自行判斷資料的種類,
1289
- 並進行對話,使用 台灣人的口與表達,及繁體中文zh-TW
1290
-
1291
- 如果是影片類型,不用解釋逐字稿格式,直接回答學生問題
1292
- 請你用蘇格拉底式的提問方式,引導學生思考,並且給予學生一些提示
1293
- 不要直接給予答案,讓學生自己思考
1294
- 但可以給予一些提示跟引導,例如給予影片的時間軸,讓學生自己去找答案
1295
-
1296
- 如果學生問了一些問題你無法判斷,請告訴學生你無法判斷,並建議學生可以問其他問題
1297
- 或者你可以問學生一些問題,幫助學生更好的理解資料
1298
-
1299
- 如果學生的問題與資料文本無關,請告訴學生你無法回答超出範圍的問題
1300
-
1301
- 最後,在你回答的開頭標註【蘇格拉底助教】
1302
- """
1303
- else:
1304
- sys_content = f"""
1305
- 你是一個擅長資料分析跟影片教學的老師,user 為學生
1306
- 請用 {data} 為資料文本,自行判斷資料的種類,
1307
- 並進行對話,使用 zh-TW
1308
-
1309
- 如果是影片類型,不用解釋逐字稿格式,直接回答學生問題
1310
- 但可以給予一些提示跟引導,例如給予影片的時間軸,讓學生可以找到相對應的時間點
1311
-
1312
- 如果學生問了一些問題你無法判斷,請告訴學生你無法判斷,並建議學生可以問其他問題
1313
- 或者你可以問學生一些問題,幫助學生更好的理解資料
1314
-
1315
- 如果學生的問題與資料文本無關,請告訴學生你無法回答超出範圍的問題
1316
- """
1317
-
1318
- messages = [
1319
- {"role": "system", "content": sys_content}
1320
- ]
1321
-
1322
- # if chat_history is not none, append role, content to messages
1323
- # chat_history = [(user, assistant), (user, assistant), ...]
1324
- # In the list, first one is user, then assistant
1325
- if chat_history is not None:
1326
- # 如果超過10則訊息,只保留最後10則訊息
1327
- if len(chat_history) > 10:
1328
- chat_history = chat_history[-10:]
1329
-
1330
- for chat in chat_history:
1331
- old_messages = [
1332
- {"role": "user", "content": chat[0]},
1333
- {"role": "assistant", "content": chat[1]}
1334
- ]
1335
- messages += old_messages
1336
- else:
1337
- pass
1338
-
1339
- messages.append({"role": "user", "content": user_message})
1340
- request_payload = {
1341
- "model": "mixtral-8x7b-32768",
1342
- "messages": messages,
1343
- "max_tokens": 4000 # 設定一個較大的值,可根據需要調整
1344
- }
1345
- response = GROQ_CLIENT.chat.completions.create(**request_payload)
1346
- response_text = response.choices[0].message.content.strip()
1347
-
1348
- # 更新聊天历史
1349
- new_chat_history = (user_message, response_text)
1350
- if chat_history is None:
1351
- chat_history = [new_chat_history]
1352
- else:
1353
- chat_history.append(new_chat_history)
1354
-
1355
- # 返回聊天历史和空字符串清空输入框
1356
- return "", chat_history
1357
-
1358
  def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
1359
  verify_password(password)
1360
 
@@ -1371,31 +1220,6 @@ def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript, user
1371
  try:
1372
  assistant_id = "asst_kmvZLNkDUYaNkMNtZEAYxyPq"
1373
  client = OPEN_AI_CLIENT
1374
-
1375
- # 從 file 拿逐字稿資料
1376
- # instructions = f"""
1377
- # 你是一個擅長資料分析跟影片教學的老師,user 為學生
1378
- # 請根據 assistant beta 的��傳資料
1379
- # 如果 file 內有找到 file.content["{youtube_id}"] 為資料文本,自行判斷資料的種類,
1380
- # 如果沒有資料,請告訴用戶沒有逐字稿資料,但仍然可以進行對話,使用台灣人的口與表達,及繁體中文 zh-TW
1381
- # 請嚴格執行,只根據 file.content["{youtube_id}"] 為資料文本,沒有就是沒有資料,不要引用其他資料
1382
-
1383
- # 如果是影片類型,不用解釋逐字稿格式,直接回答學生問題
1384
- # socratic_mode = {socratic_mode}
1385
- # 如果 socratic_mode = True,
1386
- # - 請用蘇格拉底式的提問方式,引導學生思考,並且給予學生一些提示
1387
- # - 不要直接給予答案,讓學生自己思考
1388
- # - 但可以給予一些提示跟引導,例如給予影片的時間軸,讓學生自己去找答案
1389
- # - 在你回答的開頭標註【蘇格拉底助教:{youtube_id} 】
1390
- # 如果 socratic_mode = False,
1391
- # - 直接回答學生問題
1392
- # - 在你回答的開頭標註【一般學習精靈:{youtube_id} 】
1393
- # 如果學生問了一些問題你無法判斷,請告訴學生你無法判斷,並建議學生可以問其他問題
1394
- # 或者你可以反問學生一些問題,幫助學生更好的理解資料
1395
- # 如果學生的問題與資料文本無關,請告訴學生你無法回答超出範圍的問題
1396
- # 最後只要是參考逐字稿資料,請在回答的最後標註【參考資料:(分):(秒)】
1397
- # """
1398
-
1399
  # 直接安排逐字稿資料 in instructions
1400
  trascript_json = json.loads(trascript)
1401
  # 移除 embed_url, screenshot_path
@@ -1549,103 +1373,6 @@ def poll_run_status(run_id, thread_id, timeout=600, poll_interval=5):
1549
 
1550
  return run.status
1551
 
1552
- def chat_with_claude3(password, video_id, trascript, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
1553
- verify_password(password)
1554
-
1555
- # 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
1556
- if chat_history is not None and len(chat_history) > 10:
1557
- error_msg = "此次對話超過上限"
1558
- raise gr.Error(error_msg)
1559
-
1560
- trascript_json = json.loads(trascript)
1561
- for entry in trascript_json:
1562
- entry.pop('embed_url', None) # Remove 'embed_url' if it exists
1563
- entry.pop('screenshot_path', None)
1564
- trascript_text = json.dumps(trascript_json, ensure_ascii=False, indent=2)
1565
-
1566
-
1567
- sys_content = f"""
1568
- 科目:{content_subject}
1569
- 年級:{content_grade}
1570
- 逐字稿資料:{trascript_text}
1571
- -------------------------------------
1572
- 你是一個專業的{content_subject}老師, user 為{content_grade}的學生
1573
- socratic_mode = {socratic_mode}
1574
- if socratic_mode is True,
1575
- - 請用蘇格拉底式的提問方式,引導學生思考,並且給予學生一些提示
1576
- - 一次只問一個問題,字數在100字以內
1577
- - 不要直接給予答案,讓學生自己思考
1578
- - 但可以給予一些提示跟引導,例如給予影片的時間軸,讓學生自己去找答案
1579
- - 在你回答的開頭標註【蘇格拉底助教:{video_id} 】
1580
-
1581
- if socratic_mode is False,
1582
- - 直接回答學生問題,字數在100字以內
1583
- - 在你回答的開頭標註【一般學習精靈:{video_id} 】
1584
-
1585
- rule:
1586
- - 請一定要用繁體中文回答 zh-TW,並用台灣人的口語表達,回答時不用特別說明這是台灣人的語氣,也不用說這是「台語的說法」
1587
- - 不用提到「逐字稿」這個詞
1588
- - 如果學生問了一些問題你無法判斷,請告訴學生你無法判斷,並建議學生可以問其他問題
1589
- - 或者你可以反問學生一些問題,幫助學生更好的理解資料,字數在100字以內
1590
- - 如果學生的問題與資料文本無關,請告訴學生你「無法回答超出影片範圍的問題」,並告訴他可以怎麼問什麼樣的問題(一個就好)
1591
- - 只要是參考逐字稿資料,請在回答的最後標註【參考資料:(分):(秒)】
1592
- - 回答範圍一定要在逐字稿資料內,不要引用其他資料,請嚴格執行
1593
- - 並在重複問句後給予學生鼓勵,讓學生有學習的動力
1594
- - 請用 {content_grade} 的學生能懂的方式回答
1595
-
1596
- """
1597
-
1598
-
1599
- messages = []
1600
- if chat_history is not None:
1601
- # 如果超過10則訊息,只保留最後10則訊息
1602
- if len(chat_history) > 10:
1603
- chat_history = chat_history[-10:]
1604
-
1605
- for chat in chat_history:
1606
- old_messages = [
1607
- {"role": "user", "content": chat[0]},
1608
- {"role": "assistant", "content": chat[1]}
1609
- ]
1610
- messages += old_messages
1611
- else:
1612
- pass
1613
-
1614
- messages.append({"role": "user", "content": user_message})
1615
- model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
1616
- # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
1617
- kwargs = {
1618
- "modelId": model_id,
1619
- "contentType": "application/json",
1620
- "accept": "application/json",
1621
- "body": json.dumps({
1622
- "anthropic_version": "bedrock-2023-05-31",
1623
- "max_tokens": 1000,
1624
- "system": sys_content,
1625
- "messages": messages
1626
- })
1627
- }
1628
- # 建立 message API,讀取回應
1629
- response = BEDROCK_CLIENT.invoke_model(**kwargs)
1630
-
1631
- try:
1632
- # 处理响应数据
1633
- response_body = json.loads(response.get('body').read())
1634
- response_completion = response_body.get('content')[0].get('text').strip()
1635
- # 更新聊天历史
1636
- new_chat_history = (user_message, response_completion)
1637
- if chat_history is None:
1638
- chat_history = [new_chat_history]
1639
- else:
1640
- chat_history.append(new_chat_history)
1641
-
1642
- # 返回聊天历史和空字符串清空输入框
1643
- return "", chat_history
1644
- except Exception as e:
1645
- # 处理错误情况
1646
- print(f"Error: {e}")
1647
- return "请求失败,请稍后再试!", chat_history
1648
-
1649
  # --- Slide mode ---
1650
  def update_slide(direction):
1651
  global TRANSCRIPTS
@@ -1784,17 +1511,26 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
1784
  msg = gr.Textbox(label="Message")
1785
  send_button = gr.Button("Send", variant="primary")
1786
  with gr.Tab("GROQ"):
 
1787
  groq_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="groq mode chatbot", show_share_button=False, likeable=True)
1788
  groq_msg = gr.Textbox(label="Message")
1789
  groq_send_button = gr.Button("Send", variant="primary")
1790
  with gr.Tab("JUTOR"):
 
1791
  jutor_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="jutor mode chatbot", show_share_button=False, likeable=True)
1792
  jutor_msg = gr.Textbox(label="Message")
1793
  jutor_send_button = gr.Button("Send", variant="primary")
1794
  with gr.Tab("CLAUDE"):
 
1795
  claude_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="claude mode chatbot", show_share_button=False, likeable=True)
1796
  claude_msg = gr.Textbox(label="Message")
1797
  claude_send_button = gr.Button("Send", variant="primary")
 
 
 
 
 
 
1798
  with gr.Tab("教師版"):
1799
  with gr.Row():
1800
  content_subject = gr.Dropdown(label="選擇主題", choices=["數學", "自然", "國文", "英文", "社會","物理", "化學", "生物", "地理", "歷史", "公民"], value="", visible=False)
@@ -1896,22 +1632,28 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
1896
  )
1897
  # GROQ 模式
1898
  groq_send_button.click(
1899
- chat_with_groq,
1900
- inputs=[password, groq_msg, df_string_output, groq_chatbot, socratic_mode_btn],
1901
  outputs=[groq_msg, groq_chatbot]
1902
  )
1903
  # JUTOR API 模式
1904
  jutor_send_button.click(
1905
- chat_with_jutor,
1906
- inputs=[password, jutor_msg, df_string_output, jutor_chatbot, socratic_mode_btn],
1907
  outputs=[jutor_msg, jutor_chatbot]
1908
  )
1909
  # CLAUDE 模式
1910
  claude_send_button.click(
1911
- chat_with_claude3,
1912
- inputs=[password, video_id, df_string_output, claude_msg, claude_chatbot, content_subject, content_grade, socratic_mode_btn],
1913
  outputs=[claude_msg, claude_chatbot]
1914
  )
 
 
 
 
 
 
1915
 
1916
  # 连接按钮点击事件
1917
  btn_1_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, df_string_output, btn_1, chatbot, content_subject, content_grade, socratic_mode_btn]
 
33
 
34
  import boto3
35
 
36
+ from chatbot import Chatbot
37
+
38
  is_env_local = os.getenv("IS_ENV_LOCAL", "false") == "true"
39
  print(f"is_env_local: {is_env_local}")
40
 
 
1163
  return word_path
1164
 
1165
  # ---- Chatbot ----
1166
+ def chat_with_ai(ai_name, password, video_id, trascript, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
1167
  verify_password(password)
1168
 
 
1169
  if chat_history is not None and len(chat_history) > 10:
1170
  error_msg = "此次對話超過上限"
1171
  raise gr.Error(error_msg)
1172
 
1173
+ if ai_name == "jutor":
1174
+ ai_client = ""
1175
+ elif ai_name == "claude3":
1176
+ ai_client = BEDROCK_CLIENT
1177
+ elif ai_name == "groq":
1178
+ ai_client = GROQ_CLIENT
1179
+
1180
+ chatbot_config = {
1181
+ "video_id": video_id,
1182
+ "trascript": trascript,
1183
+ "content_subject": content_subject,
1184
+ "content_grade": content_grade,
1185
+ "jutor_chat_key": JUTOR_CHAT_KEY,
1186
+ "ai_name": ai_name,
1187
+ "ai_client": ai_client
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1188
  }
1189
+ chatbot = Chatbot(chatbot_config)
1190
+ response_completion = chatbot.chat(user_message, chat_history, socratic_mode, ai_name)
1191
 
1192
+ try:
 
 
 
 
 
1193
  # 更新聊天历史
1194
+ new_chat_history = (user_message, response_completion)
1195
  if chat_history is None:
1196
  chat_history = [new_chat_history]
1197
  else:
 
1199
 
1200
  # 返回聊天历史和空字符串清空输入框
1201
  return "", chat_history
1202
+ except Exception as e:
1203
  # 处理错误情况
1204
+ print(f"Error: {e}")
1205
  return "请求失败,请稍后再试!", chat_history
1206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1207
  def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
1208
  verify_password(password)
1209
 
 
1220
  try:
1221
  assistant_id = "asst_kmvZLNkDUYaNkMNtZEAYxyPq"
1222
  client = OPEN_AI_CLIENT
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1223
  # 直接安排逐字稿資料 in instructions
1224
  trascript_json = json.loads(trascript)
1225
  # 移除 embed_url, screenshot_path
 
1373
 
1374
  return run.status
1375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1376
  # --- Slide mode ---
1377
  def update_slide(direction):
1378
  global TRANSCRIPTS
 
1511
  msg = gr.Textbox(label="Message")
1512
  send_button = gr.Button("Send", variant="primary")
1513
  with gr.Tab("GROQ"):
1514
+ groq_ai_name = gr.Textbox(label="AI 助理名稱", value="groq", visible=False)
1515
  groq_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="groq mode chatbot", show_share_button=False, likeable=True)
1516
  groq_msg = gr.Textbox(label="Message")
1517
  groq_send_button = gr.Button("Send", variant="primary")
1518
  with gr.Tab("JUTOR"):
1519
+ jutor_ai_name = gr.Textbox(label="AI 助理名稱", value="jutor", visible=False)
1520
  jutor_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="jutor mode chatbot", show_share_button=False, likeable=True)
1521
  jutor_msg = gr.Textbox(label="Message")
1522
  jutor_send_button = gr.Button("Send", variant="primary")
1523
  with gr.Tab("CLAUDE"):
1524
+ claude_ai_name = gr.Textbox(label="AI 助理名稱", value="claude3", visible=False)
1525
  claude_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="claude mode chatbot", show_share_button=False, likeable=True)
1526
  claude_msg = gr.Textbox(label="Message")
1527
  claude_send_button = gr.Button("Send", variant="primary")
1528
+ with gr.Tab("ai_chatbot"):
1529
+ ai_name = gr.Dropdown(label="選擇 AI 助理", choices=["jutor", "claude3", "groq"], value="jutor")
1530
+ ai_chatbot = gr.Chatbot(avatar_images=[bot_avatar, user_avatar], label="ai_chatbot", show_share_button=False, likeable=True)
1531
+ ai_msg = gr.Textbox(label="Message")
1532
+ ai_send_button = gr.Button("Send", variant="primary")
1533
+
1534
  with gr.Tab("教師版"):
1535
  with gr.Row():
1536
  content_subject = gr.Dropdown(label="選擇主題", choices=["數學", "自然", "國文", "英文", "社會","物理", "化學", "生物", "地理", "歷史", "公民"], value="", visible=False)
 
1632
  )
1633
  # GROQ 模式
1634
  groq_send_button.click(
1635
+ chat_with_ai,
1636
+ inputs=[groq_ai_name, password, video_id, df_string_output, groq_msg, groq_chatbot, content_subject, content_grade, socratic_mode_btn],
1637
  outputs=[groq_msg, groq_chatbot]
1638
  )
1639
  # JUTOR API 模式
1640
  jutor_send_button.click(
1641
+ chat_with_ai,
1642
+ inputs=[jutor_ai_name, password, video_id, df_string_output, jutor_msg, jutor_chatbot, content_subject, content_grade, socratic_mode_btn],
1643
  outputs=[jutor_msg, jutor_chatbot]
1644
  )
1645
  # CLAUDE 模式
1646
  claude_send_button.click(
1647
+ chat_with_ai,
1648
+ inputs=[claude_ai_name, password, video_id, df_string_output, claude_msg, claude_chatbot, content_subject, content_grade, socratic_mode_btn],
1649
  outputs=[claude_msg, claude_chatbot]
1650
  )
1651
+ # ai_chatbot 模式
1652
+ ai_send_button.click(
1653
+ chat_with_ai,
1654
+ inputs=[ai_name, password, video_id, df_string_output, ai_msg, ai_chatbot, content_subject, content_grade, socratic_mode_btn],
1655
+ outputs=[ai_msg, ai_chatbot]
1656
+ )
1657
 
1658
  # 连接按钮点击事件
1659
  btn_1_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, df_string_output, btn_1, chatbot, content_subject, content_grade, socratic_mode_btn]
chatbot.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ import requests
4
+
5
+ class Chatbot:
6
+ def __init__(self, config):
7
+ self.video_id = config.get('video_id')
8
+ self.content_subject = config.get('content_subject')
9
+ self.content_grade = config.get('content_grade')
10
+ self.jutor_chat_key = config.get('jutor_chat_key')
11
+ self.transcript_text = self.get_transcript_text(config.get('trascript'))
12
+ self.ai_name = config.get('ai_name')
13
+ self.ai_client = config.get('ai_client')
14
+
15
+ def get_transcript_text(self, transcript_data):
16
+ transcript_json = json.loads(transcript_data)
17
+ for entry in transcript_json:
18
+ entry.pop('embed_url', None)
19
+ entry.pop('screenshot_path', None)
20
+ transcript_text = json.dumps(transcript_json, ensure_ascii=False)
21
+ return transcript_text
22
+
23
+ def chat(self, user_message, chat_history, socratic_mode=False, service_type='jutor'):
24
+ messages = self.prepare_messages(chat_history, user_message)
25
+ system_prompt = self.prepare_system_prompt(socratic_mode)
26
+ if service_type in ['jutor', 'groq', 'claude3']:
27
+ response_text = self.chat_with_service(service_type, system_prompt, messages)
28
+ return response_text
29
+ else:
30
+ raise gr.Error("不支持此服務")
31
+
32
+ def prepare_system_prompt(self, socratic_mode):
33
+ content_subject = self.content_subject
34
+ content_grade = self.content_grade
35
+ video_id = self.video_id
36
+ trascript_text = self.transcript_text
37
+ socratic_mode = str(socratic_mode)
38
+ ai_name = self.ai_name
39
+ system_prompt = f"""
40
+ 科目:{content_subject}
41
+ 年級:{content_grade}
42
+ 逐字稿資料:{trascript_text}
43
+ -------------------------------------
44
+ 你是一個專業的{content_subject}老師, user 為{content_grade}的學生
45
+ socratic_mode = {socratic_mode}
46
+ if socratic_mode is True,
47
+ - 請用蘇格拉底式的提問方式,引導學生思考,並且給予學生一些提示
48
+ - 一次只問一個問題,字數在100字以內
49
+ - 不要直接給予答案,讓學生自己思考
50
+ - 但可以給予一些提示跟引導,例如給予影片的時間軸,讓學生自己去找答案
51
+ - 在你回答的開頭標註【{ai_name}|蘇格拉底助教:{video_id} 】
52
+
53
+ if socratic_mode is False,
54
+ - 直接回答學生問題,字數在100字以內
55
+ - 在你回答的開頭標註【{ai_name}|一般學習精靈:{video_id} 】
56
+
57
+ rule:
58
+ - 請一定要用繁體中文回答 zh-TW,並用台灣人的口語表達,回答時不用特別說明這是台灣人的語氣,也不用說這是「台語的說法」
59
+ - 不用提到「逐字稿」這個詞
60
+ - 如果學生問了一些問題你無法判斷,請告訴學生你無法判斷,並建議學生可以問其他問題
61
+ - 或者你可以反問學生一些問題,幫助學生更好的理解資料,字數在100字以內
62
+ - 如果學生的問題與資料文本無關,請告訴學生你「無法回答超出影片範圍的問題」,並告訴他可以怎麼問什麼樣的問題(一個就好)
63
+ - 只要是參考逐字稿資料,請在回答的最後標註【參考資料:(分):(秒)】
64
+ - 回答範圍一定要在逐字稿資料內,不要引用其他資料,請嚴格執行
65
+ - 並在重複問句後給予學生鼓勵,讓學生有學習的動力
66
+ - 請用 {content_grade} 的學生能懂的方式回答
67
+ """
68
+
69
+ return system_prompt
70
+
71
+ def prepare_messages(self, chat_history, user_message):
72
+ messages = []
73
+ if chat_history is not None:
74
+ if len(chat_history) > 10:
75
+ chat_history = chat_history[-10:]
76
+
77
+ for user_msg, assistant_msg in chat_history:
78
+ if user_msg:
79
+ messages.append({"role": "user", "content": user_msg})
80
+ if assistant_msg:
81
+ messages.append({"role": "assistant", "content": assistant_msg})
82
+
83
+ if user_message:
84
+ user_message += "/n (請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣)"
85
+ messages.append({"role": "user", "content": user_message})
86
+ return messages
87
+
88
+ def chat_with_service(self, service_type, system_prompt, messages):
89
+ if service_type == 'jutor':
90
+ return self.chat_with_jutor(system_prompt, messages)
91
+ elif service_type == 'groq':
92
+ return self.chat_with_groq(system_prompt, messages)
93
+ elif service_type == 'claude3':
94
+ return self.chat_with_claude3(system_prompt, messages)
95
+ else:
96
+ raise gr.Error("不支持的服务类型")
97
+
98
+ def chat_with_jutor(self, system_prompt, messages):
99
+ messages.insert(0, {"role": "system", "content": system_prompt})
100
+ api_endpoint = "https://ci-live-feat-video-ai-dot-junyiacademy.appspot.com/api/v2/jutor/hf-chat"
101
+ headers = {
102
+ "Content-Type": "application/json",
103
+ "x-api-key": self.jutor_chat_key,
104
+ }
105
+ data = {
106
+ "data": {
107
+ "messages": messages,
108
+ "max_tokens": 512,
109
+ "temperature": 0.9,
110
+ "model": "gpt-4-1106-preview",
111
+ "stream": False,
112
+ }
113
+ }
114
+
115
+ response = requests.post(api_endpoint, headers=headers, data=json.dumps(data))
116
+ response_data = response.json()
117
+ response_completion = response_data['data']['choices'][0]['message']['content'].strip()
118
+ return response_completion
119
+
120
+ def chat_with_groq(self, system_prompt, messages):
121
+ # system_prompt insert to messages 的最前面 {"role": "system", "content": system_prompt}
122
+ messages.insert(0, {"role": "system", "content": system_prompt})
123
+ request_payload = {
124
+ "model": "mixtral-8x7b-32768",
125
+ "messages": messages,
126
+ "max_tokens": 1000 # 設定一個較大的值,可根據需要調整
127
+ }
128
+ groq_client = self.ai_client
129
+ response = groq_client.chat.completions.create(**request_payload)
130
+ response_completion = response.choices[0].message.content.strip()
131
+ return response_completion
132
+
133
+ def chat_with_claude3(self, system_prompt, messages):
134
+ if not system_prompt.strip():
135
+ raise ValueError("System prompt cannot be empty")
136
+
137
+ model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
138
+ # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
139
+ kwargs = {
140
+ "modelId": model_id,
141
+ "contentType": "application/json",
142
+ "accept": "application/json",
143
+ "body": json.dumps({
144
+ "anthropic_version": "bedrock-2023-05-31",
145
+ "max_tokens": 1000,
146
+ "system": system_prompt,
147
+ "messages": messages
148
+ })
149
+ }
150
+ print(messages)
151
+ # 建立 message API,讀取回應
152
+ bedrock_client = self.ai_client
153
+ response = bedrock_client.invoke_model(**kwargs)
154
+ response_body = json.loads(response.get('body').read())
155
+ response_completion = response_body.get('content')[0].get('text').strip()
156
+ return response_completion