Spaces:
Sleeping
Sleeping
update
Browse files- app.py +303 -114
- chatbot.py +1 -1
- educational_material.py +1 -1
- requirements.txt +1 -1
app.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import pandas as pd
|
| 3 |
import requests
|
| 4 |
-
from bs4 import BeautifulSoup
|
| 5 |
from docx import Document
|
| 6 |
import os
|
| 7 |
from openai import OpenAI
|
|
@@ -22,10 +21,11 @@ import os
|
|
| 22 |
import io
|
| 23 |
import time
|
| 24 |
import json
|
| 25 |
-
from datetime import timedelta
|
| 26 |
from urllib.parse import urlparse, parse_qs
|
| 27 |
|
| 28 |
from google.cloud import storage
|
|
|
|
| 29 |
from google.oauth2 import service_account
|
| 30 |
from googleapiclient.discovery import build
|
| 31 |
from googleapiclient.http import MediaFileUpload
|
|
@@ -53,6 +53,7 @@ if is_env_local:
|
|
| 53 |
PASSWORD = config["PASSWORD"]
|
| 54 |
GCS_KEY = json.dumps(config["GOOGLE_APPLICATION_CREDENTIALS_JSON"])
|
| 55 |
DRIVE_KEY = json.dumps(config["GOOGLE_APPLICATION_CREDENTIALS_JSON"])
|
|
|
|
| 56 |
OPEN_AI_KEY = config["OPEN_AI_KEY"]
|
| 57 |
OPEN_AI_ASSISTANT_ID_GPT4_BOT1 = config["OPEN_AI_ASSISTANT_ID_GPT4_BOT1"]
|
| 58 |
OPEN_AI_ASSISTANT_ID_GPT3_BOT1 = config["OPEN_AI_ASSISTANT_ID_GPT3_BOT1"]
|
|
@@ -71,6 +72,7 @@ else:
|
|
| 71 |
PASSWORD = os.getenv("PASSWORD")
|
| 72 |
GCS_KEY = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON")
|
| 73 |
DRIVE_KEY = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON")
|
|
|
|
| 74 |
OPEN_AI_KEY = os.getenv("OPEN_AI_KEY")
|
| 75 |
OPEN_AI_ASSISTANT_ID_GPT4_BOT1 = os.getenv("OPEN_AI_ASSISTANT_ID_GPT4_BOT1")
|
| 76 |
OPEN_AI_ASSISTANT_ID_GPT3_BOT1 = os.getenv("OPEN_AI_ASSISTANT_ID_GPT3_BOT1")
|
|
@@ -86,9 +88,10 @@ else:
|
|
| 86 |
|
| 87 |
TRANSCRIPTS = []
|
| 88 |
CURRENT_INDEX = 0
|
| 89 |
-
CHAT_LIMIT =
|
| 90 |
|
| 91 |
# CLIENTS CONFIG
|
|
|
|
| 92 |
GROQ_CLIENT = Groq(api_key=GROQ_API_KEY)
|
| 93 |
GCS_SERVICE = GoogleCloudStorage(GCS_KEY)
|
| 94 |
GCS_CLIENT = GCS_SERVICE.client
|
|
@@ -706,7 +709,9 @@ def split_data(df_string, word_base=100000):
|
|
| 706 |
def generate_content_by_open_ai(sys_content, user_content, response_format=None):
|
| 707 |
print("LLM using OPEN AI")
|
| 708 |
# model = "gpt-4-turbo"
|
| 709 |
-
model = "gpt-4o"
|
|
|
|
|
|
|
| 710 |
messages = [
|
| 711 |
{"role": "system", "content": sys_content},
|
| 712 |
{"role": "user", "content": user_content}
|
|
@@ -724,39 +729,40 @@ def generate_content_by_open_ai(sys_content, user_content, response_format=None)
|
|
| 724 |
content = response.choices[0].message.content.strip()
|
| 725 |
return content
|
| 726 |
|
| 727 |
-
def generate_content_by_bedrock(sys_content, user_content):
|
| 728 |
-
|
| 729 |
-
|
| 730 |
-
|
| 731 |
-
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
|
| 744 |
-
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
|
|
|
| 749 |
|
| 750 |
def generate_content_by_LLM(sys_content, user_content, response_format=None, LLM_model=None):
|
| 751 |
# 使用 OpenAI 生成基于上传数据的问题
|
| 752 |
|
| 753 |
-
if LLM_model == "anthropic-claude-3-sonnet":
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
else:
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
print("=====content=====")
|
| 761 |
print(content)
|
| 762 |
print("=====content=====")
|
|
@@ -1221,7 +1227,6 @@ def change_questions(password, df_string):
|
|
| 1221 |
def get_key_moments(video_id, formatted_simple_transcript, formatted_transcript, source, LLM_model=None):
|
| 1222 |
if source == "gcs":
|
| 1223 |
print("===get_key_moments on gcs===")
|
| 1224 |
-
gcs_client = GCS_CLIENT
|
| 1225 |
bucket_name = 'video_ai_assistant'
|
| 1226 |
file_name = f'{video_id}_key_moments.json'
|
| 1227 |
blob_name = f"{video_id}/{file_name}"
|
|
@@ -1254,6 +1259,18 @@ def get_key_moments(video_id, formatted_simple_transcript, formatted_transcript,
|
|
| 1254 |
GCS_SERVICE.upload_json_string(bucket_name, blob_name, key_moments_text)
|
| 1255 |
key_moments_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
|
| 1256 |
key_moments_json = json.loads(key_moments_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1257 |
|
| 1258 |
elif source == "drive":
|
| 1259 |
print("===get_key_moments on drive===")
|
|
@@ -1329,6 +1346,13 @@ def generate_key_moments(formatted_simple_transcript, formatted_transcript, LLM_
|
|
| 1329 |
if start_time <= parse_time(time) <= end_time]
|
| 1330 |
moment['images'] = moment_images
|
| 1331 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1332 |
all_content += key_moments
|
| 1333 |
|
| 1334 |
return all_content
|
|
@@ -1352,6 +1376,51 @@ def generate_key_moments_keywords(transcript, LLM_model=None):
|
|
| 1352 |
|
| 1353 |
return all_content
|
| 1354 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1355 |
def get_key_moments_html(key_moments):
|
| 1356 |
css = """
|
| 1357 |
<style>
|
|
@@ -1534,7 +1603,7 @@ def get_key_moments_html(key_moments):
|
|
| 1534 |
key_moments_html = css
|
| 1535 |
|
| 1536 |
for i, moment in enumerate(key_moments):
|
| 1537 |
-
images = moment['
|
| 1538 |
image_elements = ""
|
| 1539 |
|
| 1540 |
for j, image in enumerate(images):
|
|
@@ -1909,7 +1978,7 @@ def get_meta_data(video_id, source="gcs"):
|
|
| 1909 |
|
| 1910 |
return meta_data_json
|
| 1911 |
|
| 1912 |
-
def get_ai_content(password, video_id, df_string, topic, grade, level, specific_feature, content_type, source="gcs"):
|
| 1913 |
verify_password(password)
|
| 1914 |
if source == "gcs":
|
| 1915 |
print("===get_ai_content on gcs===")
|
|
@@ -1955,10 +2024,27 @@ def get_ai_content(password, video_id, df_string, topic, grade, level, specific_
|
|
| 1955 |
ai_content_text = json.dumps(ai_content_list, ensure_ascii=False, indent=2)
|
| 1956 |
GCS_SERVICE.upload_json_string(bucket_name, blob_name, ai_content_text)
|
| 1957 |
print("ai_content已上傳到GCS")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1958 |
else:
|
| 1959 |
ai_content_json = ai_content_json[-1]
|
| 1960 |
ai_content = ai_content_json["content"]
|
| 1961 |
prompt = ai_content_json["prompt"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1962 |
|
| 1963 |
return ai_content, ai_content, prompt, prompt
|
| 1964 |
|
|
@@ -1977,7 +2063,7 @@ def generate_ai_content(password, df_string, topic, grade, level, specific_featu
|
|
| 1977 |
|
| 1978 |
return ai_content, prompt
|
| 1979 |
|
| 1980 |
-
def
|
| 1981 |
verify_password(password)
|
| 1982 |
material = EducationalMaterial(df_string_output, "", "", "", "", "")
|
| 1983 |
try:
|
|
@@ -1985,6 +2071,20 @@ def generate_exam_fine_tune_result(password, exam_result_prompt , df_string_outp
|
|
| 1985 |
except:
|
| 1986 |
fine_tuned_ai_content = material.get_fine_tuned_ai_content(BEDROCK_CLIENT, "bedrock", exam_result_prompt, exam_result, exam_result_fine_tune_prompt)
|
| 1987 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1988 |
return fine_tuned_ai_content
|
| 1989 |
|
| 1990 |
def return_original_exam_result(exam_result_original):
|
|
@@ -2059,6 +2159,7 @@ def get_instructions(content_subject, content_grade, key_moments, socratic_mode=
|
|
| 2059 |
def chat_with_any_ai(ai_type, password, video_id, user_data, transcript_state, key_moments, user_message, chat_history, content_subject, content_grade, questions_answers_json, socratic_mode=False, thread_id=None, ai_name=None):
|
| 2060 |
print(f"ai_type: {ai_type}")
|
| 2061 |
print(f"user_data: {user_data}")
|
|
|
|
| 2062 |
verify_password(password)
|
| 2063 |
verify_message_length(user_message, max_length=1500)
|
| 2064 |
|
|
@@ -2076,7 +2177,21 @@ def chat_with_any_ai(ai_type, password, video_id, user_data, transcript_state, k
|
|
| 2076 |
chatbot_config = get_chatbot_config(ai_name, transcript_state, key_moments, content_subject, content_grade, video_id, socratic_mode)
|
| 2077 |
chatbot = Chatbot(chatbot_config)
|
| 2078 |
response_text = chatbot.chat(user_message, chat_history)
|
| 2079 |
-
thread_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2080 |
elif ai_type == "assistant":
|
| 2081 |
client = OPEN_AI_CLIENT
|
| 2082 |
assistant_id = OPEN_AI_ASSISTANT_ID_GPT4 #GPT 4 turbo
|
|
@@ -2109,6 +2224,23 @@ def chat_with_any_ai(ai_type, password, video_id, user_data, transcript_state, k
|
|
| 2109 |
chat_history = update_chat_history(user_message, response_text, chat_history)
|
| 2110 |
send_btn_update, send_feedback_btn_update = update_send_and_feedback_buttons(chat_history, CHAT_LIMIT)
|
| 2111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2112 |
# 返回聊天历史和空字符串清空输入框
|
| 2113 |
return "", chat_history, send_btn_update, send_feedback_btn_update, thread_id
|
| 2114 |
|
|
@@ -2122,10 +2254,15 @@ def get_chatbot_config(ai_name, transcript_state, key_moments, content_subject,
|
|
| 2122 |
"ai_client": GROQ_CLIENT,
|
| 2123 |
"ai_model_name": "groq_llama3",
|
| 2124 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2125 |
"lili": {
|
| 2126 |
"ai_name": "lili",
|
| 2127 |
-
"ai_client":
|
| 2128 |
-
"ai_model_name": "
|
| 2129 |
},
|
| 2130 |
"maimai": {
|
| 2131 |
"ai_name": "maimai",
|
|
@@ -2168,7 +2305,7 @@ def get_chatbot_config(ai_name, transcript_state, key_moments, content_subject,
|
|
| 2168 |
|
| 2169 |
return chatbot_config
|
| 2170 |
|
| 2171 |
-
def feedback_with_ai(ai_type, chat_history, thread_id=None):
|
| 2172 |
# prompt: 請依據以上的對話(chat_history),總結我的「提問力」,並給予我是否有「問對問題」的回饋和建議
|
| 2173 |
system_content = """
|
| 2174 |
你是一個擅長引導問答素養的老師,user 為學生的提問跟回答,請精讀對話過程,針對 user 給予回饋就好,根據以下 Rule:
|
|
@@ -2217,6 +2354,22 @@ def feedback_with_ai(ai_type, chat_history, thread_id=None):
|
|
| 2217 |
chat_history = update_chat_history(feedback_request_message, response_text, chat_history)
|
| 2218 |
feedback_btn_update = gr.update(value="已回饋", interactive=False, variant="secondary")
|
| 2219 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2220 |
return chat_history, feedback_btn_update
|
| 2221 |
|
| 2222 |
def handle_conversation_by_open_ai_chat_completions(client, model_name, user_content, system_content):
|
|
@@ -2264,6 +2417,7 @@ def handle_conversation_by_open_ai_assistant(client, user_message, instructions,
|
|
| 2264 |
|
| 2265 |
if run_status == "completed":
|
| 2266 |
messages = client.beta.threads.messages.list(thread_id=thread.id)
|
|
|
|
| 2267 |
response_text = messages.data[0].content[0].text.value
|
| 2268 |
else:
|
| 2269 |
response_text = "學習精靈有點累,請稍後再試!"
|
|
@@ -2492,17 +2646,17 @@ def chat_with_opan_ai_assistant_streaming(user_message, chat_history, password,
|
|
| 2492 |
else:
|
| 2493 |
thread = client.beta.threads.retrieve(thread_id)
|
| 2494 |
print(f"old thread_id: {thread_id}")
|
| 2495 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2496 |
client.beta.threads.update(
|
| 2497 |
thread_id=thread_id,
|
| 2498 |
-
metadata=
|
| 2499 |
-
"youtube_id": video_id,
|
| 2500 |
-
"user_data": user_data,
|
| 2501 |
-
"content_subject": content_subject,
|
| 2502 |
-
"content_grade": content_grade,
|
| 2503 |
-
"assistant_id": assistant_id,
|
| 2504 |
-
"is_streaming": "true",
|
| 2505 |
-
}
|
| 2506 |
)
|
| 2507 |
|
| 2508 |
# 向线程添加用户的消息
|
|
@@ -2527,6 +2681,22 @@ def chat_with_opan_ai_assistant_streaming(user_message, chat_history, password,
|
|
| 2527 |
except Exception as e:
|
| 2528 |
print(f"Error: {e}")
|
| 2529 |
raise gr.Error(f"Error: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2530 |
|
| 2531 |
def create_thread_id():
|
| 2532 |
thread = OPEN_AI_CLIENT.beta.threads.create()
|
|
@@ -2571,6 +2741,26 @@ def show_all_chatbot_accordion():
|
|
| 2571 |
all_chatbot_select_btn_visible = gr.update(visible=False)
|
| 2572 |
return chatbot_select_accordion_visible, all_chatbot_select_btn_visible
|
| 2573 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2574 |
|
| 2575 |
# --- Init params ---
|
| 2576 |
def init_params(text, request: gr.Request):
|
|
@@ -2866,19 +3056,18 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
| 2866 |
worksheet_algorithm = gr.Dropdown(label="選擇教學策略或理論", choices=["Bloom認知階層理論", "Polya數學解題法", "CRA教學法"], value="Bloom認知階層理論", visible=False)
|
| 2867 |
worksheet_content_btn = gr.Button("生成學習單 📄", variant="primary", visible=True)
|
| 2868 |
with gr.Accordion("微調", open=False):
|
| 2869 |
-
|
| 2870 |
-
|
| 2871 |
-
|
| 2872 |
with gr.Accordion("prompt", open=False) as worksheet_accordion:
|
| 2873 |
worksheet_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
| 2874 |
with gr.Column(scale=2):
|
| 2875 |
# 生成對應不同模式的結果
|
| 2876 |
-
|
| 2877 |
-
|
| 2878 |
-
|
| 2879 |
-
|
| 2880 |
-
|
| 2881 |
-
worksheet_exam_result_word_link = gr.File(label="Download Word")
|
| 2882 |
with gr.Tab("教案"):
|
| 2883 |
with gr.Row():
|
| 2884 |
with gr.Column(scale=1):
|
|
@@ -2887,19 +3076,19 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
| 2887 |
lesson_plan_time = gr.Slider(label="選擇課程時間(分鐘)", minimum=10, maximum=120, step=5, value=40)
|
| 2888 |
lesson_plan_btn = gr.Button("生成教案 📕", variant="primary", visible=True)
|
| 2889 |
with gr.Accordion("微調", open=False):
|
| 2890 |
-
|
| 2891 |
-
|
| 2892 |
-
|
| 2893 |
with gr.Accordion("prompt", open=False) as lesson_plan_accordion:
|
| 2894 |
lesson_plan_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
| 2895 |
with gr.Column(scale=2):
|
| 2896 |
# 生成對應不同模式的結果
|
| 2897 |
-
|
| 2898 |
-
|
| 2899 |
-
|
| 2900 |
|
| 2901 |
-
|
| 2902 |
-
|
| 2903 |
with gr.Tab("出場券"):
|
| 2904 |
with gr.Row():
|
| 2905 |
with gr.Column(scale=1):
|
|
@@ -2908,19 +3097,19 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
| 2908 |
exit_ticket_time = gr.Slider(label="選擇出場券時間(分鐘)", minimum=5, maximum=10, step=1, value=8)
|
| 2909 |
exit_ticket_btn = gr.Button("生成出場券 🎟️", variant="primary", visible=True)
|
| 2910 |
with gr.Accordion("微調", open=False):
|
| 2911 |
-
|
| 2912 |
-
|
| 2913 |
-
|
| 2914 |
with gr.Accordion("prompt", open=False) as exit_ticket_accordion:
|
| 2915 |
exit_ticket_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
| 2916 |
with gr.Column(scale=2):
|
| 2917 |
# 生成對應不同模式的結果
|
| 2918 |
-
|
| 2919 |
-
|
| 2920 |
-
|
| 2921 |
|
| 2922 |
-
|
| 2923 |
-
|
| 2924 |
|
| 2925 |
|
| 2926 |
# with gr.Tab("素養導向閱讀題組"):
|
|
@@ -2940,7 +3129,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
| 2940 |
with gr.Accordion("See Details", open=False) as see_details:
|
| 2941 |
with gr.Row():
|
| 2942 |
is_env_prod = gr.Checkbox(value=False, label="is_env_prod")
|
| 2943 |
-
LLM_model = gr.Dropdown(label="LLM Model", choices=["open-ai-gpt-
|
| 2944 |
with gr.Tab("逐字稿本文"):
|
| 2945 |
with gr.Row() as transcript_admmin:
|
| 2946 |
transcript_kind = gr.Textbox(value="transcript", show_label=False)
|
|
@@ -3131,7 +3320,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
| 3131 |
)
|
| 3132 |
ai_send_feedback_btn.click(
|
| 3133 |
feedback_with_ai,
|
| 3134 |
-
inputs=[ai_chatbot_ai_type, ai_chatbot, ai_chatbot_thread_id],
|
| 3135 |
outputs=[ai_chatbot, ai_send_feedback_btn],
|
| 3136 |
scroll_to_output=True
|
| 3137 |
)
|
|
@@ -3468,76 +3657,76 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
| 3468 |
{
|
| 3469 |
"button": worksheet_content_btn,
|
| 3470 |
"action": get_ai_content,
|
| 3471 |
-
"inputs": [password, video_id, df_string_output, content_subject, content_grade, content_level, worksheet_algorithm, worksheet_content_type_name],
|
| 3472 |
-
"outputs": [
|
| 3473 |
},
|
| 3474 |
{
|
| 3475 |
-
"button":
|
| 3476 |
-
"action":
|
| 3477 |
-
"inputs": [password,
|
| 3478 |
-
"outputs": [
|
| 3479 |
},
|
| 3480 |
{
|
| 3481 |
-
"button":
|
| 3482 |
"action": download_exam_result,
|
| 3483 |
-
"inputs": [
|
| 3484 |
-
"outputs": [
|
| 3485 |
},
|
| 3486 |
{
|
| 3487 |
-
"button":
|
| 3488 |
"action": return_original_exam_result,
|
| 3489 |
-
"inputs": [
|
| 3490 |
-
"outputs": [
|
| 3491 |
},
|
| 3492 |
# 教案相關按鈕
|
| 3493 |
{
|
| 3494 |
"button": lesson_plan_btn,
|
| 3495 |
"action": get_ai_content,
|
| 3496 |
-
"inputs": [password, video_id, df_string_output, content_subject, content_grade, content_level, lesson_plan_time, lesson_plan_content_type_name],
|
| 3497 |
-
"outputs": [
|
| 3498 |
},
|
| 3499 |
{
|
| 3500 |
-
"button":
|
| 3501 |
-
"action":
|
| 3502 |
-
"inputs": [password,
|
| 3503 |
-
"outputs": [
|
| 3504 |
},
|
| 3505 |
{
|
| 3506 |
-
"button":
|
| 3507 |
"action": download_exam_result,
|
| 3508 |
-
"inputs": [
|
| 3509 |
-
"outputs": [
|
| 3510 |
},
|
| 3511 |
{
|
| 3512 |
-
"button":
|
| 3513 |
"action": return_original_exam_result,
|
| 3514 |
-
"inputs": [
|
| 3515 |
-
"outputs": [
|
| 3516 |
},
|
| 3517 |
# 出場券相關按鈕
|
| 3518 |
{
|
| 3519 |
"button": exit_ticket_btn,
|
| 3520 |
"action": get_ai_content,
|
| 3521 |
-
"inputs": [password, video_id, df_string_output, content_subject, content_grade, content_level, exit_ticket_time, exit_ticket_content_type_name],
|
| 3522 |
-
"outputs": [
|
| 3523 |
},
|
| 3524 |
{
|
| 3525 |
-
"button":
|
| 3526 |
-
"action":
|
| 3527 |
-
"inputs": [password,
|
| 3528 |
-
"outputs": [
|
| 3529 |
},
|
| 3530 |
{
|
| 3531 |
-
"button":
|
| 3532 |
"action": download_exam_result,
|
| 3533 |
-
"inputs": [
|
| 3534 |
-
"outputs": [
|
| 3535 |
},
|
| 3536 |
{
|
| 3537 |
-
"button":
|
| 3538 |
"action": return_original_exam_result,
|
| 3539 |
-
"inputs": [
|
| 3540 |
-
"outputs": [
|
| 3541 |
}
|
| 3542 |
]
|
| 3543 |
setup_education_buttons(education_buttons_config)
|
|
@@ -3564,4 +3753,4 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
| 3564 |
outputs = init_outputs
|
| 3565 |
)
|
| 3566 |
|
| 3567 |
-
demo.launch(allowed_paths=["videos"])
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import pandas as pd
|
| 3 |
import requests
|
|
|
|
| 4 |
from docx import Document
|
| 5 |
import os
|
| 6 |
from openai import OpenAI
|
|
|
|
| 21 |
import io
|
| 22 |
import time
|
| 23 |
import json
|
| 24 |
+
from datetime import datetime, timezone, timedelta
|
| 25 |
from urllib.parse import urlparse, parse_qs
|
| 26 |
|
| 27 |
from google.cloud import storage
|
| 28 |
+
from google.cloud import bigquery
|
| 29 |
from google.oauth2 import service_account
|
| 30 |
from googleapiclient.discovery import build
|
| 31 |
from googleapiclient.http import MediaFileUpload
|
|
|
|
| 53 |
PASSWORD = config["PASSWORD"]
|
| 54 |
GCS_KEY = json.dumps(config["GOOGLE_APPLICATION_CREDENTIALS_JSON"])
|
| 55 |
DRIVE_KEY = json.dumps(config["GOOGLE_APPLICATION_CREDENTIALS_JSON"])
|
| 56 |
+
GBQ_KEY = json.dumps(config["GOOGLE_APPLICATION_CREDENTIALS_JSON"])
|
| 57 |
OPEN_AI_KEY = config["OPEN_AI_KEY"]
|
| 58 |
OPEN_AI_ASSISTANT_ID_GPT4_BOT1 = config["OPEN_AI_ASSISTANT_ID_GPT4_BOT1"]
|
| 59 |
OPEN_AI_ASSISTANT_ID_GPT3_BOT1 = config["OPEN_AI_ASSISTANT_ID_GPT3_BOT1"]
|
|
|
|
| 72 |
PASSWORD = os.getenv("PASSWORD")
|
| 73 |
GCS_KEY = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON")
|
| 74 |
DRIVE_KEY = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON")
|
| 75 |
+
GBQ_KEY = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON")
|
| 76 |
OPEN_AI_KEY = os.getenv("OPEN_AI_KEY")
|
| 77 |
OPEN_AI_ASSISTANT_ID_GPT4_BOT1 = os.getenv("OPEN_AI_ASSISTANT_ID_GPT4_BOT1")
|
| 78 |
OPEN_AI_ASSISTANT_ID_GPT3_BOT1 = os.getenv("OPEN_AI_ASSISTANT_ID_GPT3_BOT1")
|
|
|
|
| 88 |
|
| 89 |
TRANSCRIPTS = []
|
| 90 |
CURRENT_INDEX = 0
|
| 91 |
+
CHAT_LIMIT = 5
|
| 92 |
|
| 93 |
# CLIENTS CONFIG
|
| 94 |
+
GBQ_CLIENT = bigquery.Client.from_service_account_info(json.loads(GBQ_KEY))
|
| 95 |
GROQ_CLIENT = Groq(api_key=GROQ_API_KEY)
|
| 96 |
GCS_SERVICE = GoogleCloudStorage(GCS_KEY)
|
| 97 |
GCS_CLIENT = GCS_SERVICE.client
|
|
|
|
| 709 |
def generate_content_by_open_ai(sys_content, user_content, response_format=None):
|
| 710 |
print("LLM using OPEN AI")
|
| 711 |
# model = "gpt-4-turbo"
|
| 712 |
+
model = "gpt-4o"
|
| 713 |
+
print(f"model: {model}")
|
| 714 |
+
|
| 715 |
messages = [
|
| 716 |
{"role": "system", "content": sys_content},
|
| 717 |
{"role": "user", "content": user_content}
|
|
|
|
| 729 |
content = response.choices[0].message.content.strip()
|
| 730 |
return content
|
| 731 |
|
| 732 |
+
# def generate_content_by_bedrock(sys_content, user_content):
|
| 733 |
+
# print("LLM using REDROCK")
|
| 734 |
+
# messages = [
|
| 735 |
+
# {"role": "user", "content": user_content +"(如果是 JSON 格式,value 的引號,請用單引號,或是用反斜線+雙引號,避免 JSON Decoder error )"}
|
| 736 |
+
# ]
|
| 737 |
+
# model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
|
| 738 |
+
# print(f"model_id: {model_id}")
|
| 739 |
+
# # model_id = "anthropic.claude-3-haiku-20240307-v1:0"
|
| 740 |
+
# kwargs = {
|
| 741 |
+
# "modelId": model_id,
|
| 742 |
+
# "contentType": "application/json",
|
| 743 |
+
# "accept": "application/json",
|
| 744 |
+
# "body": json.dumps({
|
| 745 |
+
# "anthropic_version": "bedrock-2023-05-31",
|
| 746 |
+
# "max_tokens": 4000,
|
| 747 |
+
# "system": sys_content,
|
| 748 |
+
# "messages": messages
|
| 749 |
+
# })
|
| 750 |
+
# }
|
| 751 |
+
# response = BEDROCK_CLIENT.invoke_model(**kwargs)
|
| 752 |
+
# response_body = json.loads(response.get('body').read())
|
| 753 |
+
# content = response_body.get('content')[0].get('text')
|
| 754 |
+
# return content
|
| 755 |
|
| 756 |
def generate_content_by_LLM(sys_content, user_content, response_format=None, LLM_model=None):
|
| 757 |
# 使用 OpenAI 生成基于上传数据的问题
|
| 758 |
|
| 759 |
+
# if LLM_model == "anthropic-claude-3-sonnet":
|
| 760 |
+
# print(f"LLM: {LLM_model}")
|
| 761 |
+
# content = generate_content_by_bedrock(sys_content, user_content)
|
| 762 |
+
# else:
|
| 763 |
+
print(f"LLM: {LLM_model}")
|
| 764 |
+
content = generate_content_by_open_ai(sys_content, user_content, response_format)
|
| 765 |
+
|
| 766 |
print("=====content=====")
|
| 767 |
print(content)
|
| 768 |
print("=====content=====")
|
|
|
|
| 1227 |
def get_key_moments(video_id, formatted_simple_transcript, formatted_transcript, source, LLM_model=None):
|
| 1228 |
if source == "gcs":
|
| 1229 |
print("===get_key_moments on gcs===")
|
|
|
|
| 1230 |
bucket_name = 'video_ai_assistant'
|
| 1231 |
file_name = f'{video_id}_key_moments.json'
|
| 1232 |
blob_name = f"{video_id}/{file_name}"
|
|
|
|
| 1259 |
GCS_SERVICE.upload_json_string(bucket_name, blob_name, key_moments_text)
|
| 1260 |
key_moments_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
|
| 1261 |
key_moments_json = json.loads(key_moments_text)
|
| 1262 |
+
# 檢查 key_moments 是否有 suggested_images
|
| 1263 |
+
print("===檢查 key_moments 是否有 suggested_images===")
|
| 1264 |
+
has_suggested_images_added = False
|
| 1265 |
+
for key_moment in key_moments_json["key_moments"]:
|
| 1266 |
+
if "suggested_images" not in key_moment:
|
| 1267 |
+
key_moment["suggested_images"] = generate_key_moments_suggested_images(key_moment)
|
| 1268 |
+
has_suggested_images_added = True
|
| 1269 |
+
if has_suggested_images_added:
|
| 1270 |
+
key_moments_text = json.dumps(key_moments_json, ensure_ascii=False, indent=2)
|
| 1271 |
+
GCS_SERVICE.upload_json_string(bucket_name, blob_name, key_moments_text)
|
| 1272 |
+
key_moments_text = GCS_SERVICE.download_as_string(bucket_name, blob_name)
|
| 1273 |
+
key_moments_json = json.loads(key_moments_text)
|
| 1274 |
|
| 1275 |
elif source == "drive":
|
| 1276 |
print("===get_key_moments on drive===")
|
|
|
|
| 1346 |
if start_time <= parse_time(time) <= end_time]
|
| 1347 |
moment['images'] = moment_images
|
| 1348 |
|
| 1349 |
+
# 檢查是否有 suggested_images
|
| 1350 |
+
if "suggested_images" not in moment:
|
| 1351 |
+
moment["suggested_images"] = generate_key_moments_suggested_images(moment, LLM_model)
|
| 1352 |
+
print("===moment_suggested_images===")
|
| 1353 |
+
print(moment["suggested_images"])
|
| 1354 |
+
print("===moment_suggested_images===")
|
| 1355 |
+
|
| 1356 |
all_content += key_moments
|
| 1357 |
|
| 1358 |
return all_content
|
|
|
|
| 1376 |
|
| 1377 |
return all_content
|
| 1378 |
|
| 1379 |
+
def generate_key_moments_suggested_images(key_moment, LLM_model=None):
|
| 1380 |
+
# Prepare the text and keywords
|
| 1381 |
+
text = key_moment["text"]
|
| 1382 |
+
keywords = ', '.join(key_moment["keywords"])
|
| 1383 |
+
images = key_moment["images"]
|
| 1384 |
+
|
| 1385 |
+
images_list_prompt = ""
|
| 1386 |
+
for i, image_url in enumerate(images):
|
| 1387 |
+
images_list_prompt += f"\n圖片 {i+1}: {image_url}"
|
| 1388 |
+
|
| 1389 |
+
# Prepare the user prompt with text and keywords
|
| 1390 |
+
sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
|
| 1391 |
+
user_content = f"""
|
| 1392 |
+
# Rule:
|
| 1393 |
+
1. 保留有圖表或是數據的圖片
|
| 1394 |
+
2.根據以下的文本和關鍵字,選擇出最合適的圖片。
|
| 1395 |
+
- 文本: {text}
|
| 1396 |
+
- 關鍵字: {keywords}
|
| 1397 |
+
3. 總是保留最後一張,除非他是一張空白圖片,或是一張沒有任何內容的圖片
|
| 1398 |
+
|
| 1399 |
+
# Restrictions:
|
| 1400 |
+
1. 不要有相似或是概念重複的圖片
|
| 1401 |
+
2. 移除整張圖片是黑色、藍色或是白色的圖片
|
| 1402 |
+
3. 移除沒有任何內容的圖片
|
| 1403 |
+
4. 不需要理會字幕的差益,只需要看圖片的內容
|
| 1404 |
+
|
| 1405 |
+
請根據這些信息,圖片列表如下:
|
| 1406 |
+
{images_list_prompt}
|
| 1407 |
+
|
| 1408 |
+
回傳 JSON LIST 就好,不用回傳任何敘述脈絡,也不要 ```json 包覆
|
| 1409 |
+
EXAMPLE:
|
| 1410 |
+
{{
|
| 1411 |
+
"suggested_images": ["圖片1的 image_url", "圖片2 的 image_url", "圖片3的 image_url"]
|
| 1412 |
+
}}
|
| 1413 |
+
"""
|
| 1414 |
+
|
| 1415 |
+
response_format = { "type": "json_object" }
|
| 1416 |
+
response = generate_content_by_LLM(sys_content, user_content, response_format, LLM_model)
|
| 1417 |
+
print("===generate_key_moments_suggested_images===")
|
| 1418 |
+
print(response)
|
| 1419 |
+
print("===generate_key_moments_suggested_images===")
|
| 1420 |
+
suggested_images = json.loads(response)["suggested_images"]
|
| 1421 |
+
|
| 1422 |
+
return suggested_images
|
| 1423 |
+
|
| 1424 |
def get_key_moments_html(key_moments):
|
| 1425 |
css = """
|
| 1426 |
<style>
|
|
|
|
| 1603 |
key_moments_html = css
|
| 1604 |
|
| 1605 |
for i, moment in enumerate(key_moments):
|
| 1606 |
+
images = moment['suggested_images']
|
| 1607 |
image_elements = ""
|
| 1608 |
|
| 1609 |
for j, image in enumerate(images):
|
|
|
|
| 1978 |
|
| 1979 |
return meta_data_json
|
| 1980 |
|
| 1981 |
+
def get_ai_content(password, user_data, video_id, df_string, topic, grade, level, specific_feature, content_type, source="gcs"):
|
| 1982 |
verify_password(password)
|
| 1983 |
if source == "gcs":
|
| 1984 |
print("===get_ai_content on gcs===")
|
|
|
|
| 2024 |
ai_content_text = json.dumps(ai_content_list, ensure_ascii=False, indent=2)
|
| 2025 |
GCS_SERVICE.upload_json_string(bucket_name, blob_name, ai_content_text)
|
| 2026 |
print("ai_content已上傳到GCS")
|
| 2027 |
+
|
| 2028 |
+
# insert_log_to_bigquery usage
|
| 2029 |
+
data_endpoint = "chat_completions"
|
| 2030 |
+
|
| 2031 |
else:
|
| 2032 |
ai_content_json = ai_content_json[-1]
|
| 2033 |
ai_content = ai_content_json["content"]
|
| 2034 |
prompt = ai_content_json["prompt"]
|
| 2035 |
+
# insert_log_to_bigquery usage
|
| 2036 |
+
data_endpoint = "gcs"
|
| 2037 |
+
|
| 2038 |
+
# send data to GBQ
|
| 2039 |
+
user_id = user_data
|
| 2040 |
+
route = "get_ai_content"
|
| 2041 |
+
endpoint = data_endpoint
|
| 2042 |
+
event_response = {"event_response": str(ai_content)}
|
| 2043 |
+
event_response_json = json.dumps(event_response)
|
| 2044 |
+
prompt = ai_content_json
|
| 2045 |
+
prompt_json = json.dumps(prompt)
|
| 2046 |
+
feature = content_type
|
| 2047 |
+
insert_log_to_bigquery(user_id, route, endpoint, event_response_json, prompt_json, feature)
|
| 2048 |
|
| 2049 |
return ai_content, ai_content, prompt, prompt
|
| 2050 |
|
|
|
|
| 2063 |
|
| 2064 |
return ai_content, prompt
|
| 2065 |
|
| 2066 |
+
def generate_ai_content_fine_tune_result(password, user_data, exam_result_prompt , df_string_output, exam_result, exam_result_fine_tune_prompt, content_type):
|
| 2067 |
verify_password(password)
|
| 2068 |
material = EducationalMaterial(df_string_output, "", "", "", "", "")
|
| 2069 |
try:
|
|
|
|
| 2071 |
except:
|
| 2072 |
fine_tuned_ai_content = material.get_fine_tuned_ai_content(BEDROCK_CLIENT, "bedrock", exam_result_prompt, exam_result, exam_result_fine_tune_prompt)
|
| 2073 |
|
| 2074 |
+
# send data to GBQ
|
| 2075 |
+
user_id = user_data
|
| 2076 |
+
route = "generate_ai_content_fine_tune_result"
|
| 2077 |
+
endpoint = "chat_completions"
|
| 2078 |
+
event_response = {"event_response": str(fine_tuned_ai_content)}
|
| 2079 |
+
event_response_json = json.dumps(event_response)
|
| 2080 |
+
prompt = {
|
| 2081 |
+
"exam_result_prompt": exam_result_prompt,
|
| 2082 |
+
"exam_result_fine_tune_prompt": exam_result_fine_tune_prompt
|
| 2083 |
+
}
|
| 2084 |
+
prompt_json = json.dumps(prompt)
|
| 2085 |
+
feature = content_type
|
| 2086 |
+
insert_log_to_bigquery(user_id, route, endpoint, event_response_json, prompt_json, feature)
|
| 2087 |
+
|
| 2088 |
return fine_tuned_ai_content
|
| 2089 |
|
| 2090 |
def return_original_exam_result(exam_result_original):
|
|
|
|
| 2159 |
def chat_with_any_ai(ai_type, password, video_id, user_data, transcript_state, key_moments, user_message, chat_history, content_subject, content_grade, questions_answers_json, socratic_mode=False, thread_id=None, ai_name=None):
|
| 2160 |
print(f"ai_type: {ai_type}")
|
| 2161 |
print(f"user_data: {user_data}")
|
| 2162 |
+
print(f"===thread_id:{thread_id}===")
|
| 2163 |
verify_password(password)
|
| 2164 |
verify_message_length(user_message, max_length=1500)
|
| 2165 |
|
|
|
|
| 2177 |
chatbot_config = get_chatbot_config(ai_name, transcript_state, key_moments, content_subject, content_grade, video_id, socratic_mode)
|
| 2178 |
chatbot = Chatbot(chatbot_config)
|
| 2179 |
response_text = chatbot.chat(user_message, chat_history)
|
| 2180 |
+
# if thread_id is none, create random thread_id + timestamp
|
| 2181 |
+
if thread_id is None or thread_id == "":
|
| 2182 |
+
thread_id = "thread_" + str(uuid.uuid4()) + str(int(time.time()))
|
| 2183 |
+
|
| 2184 |
+
print(f"===thread_id:{thread_id}===")
|
| 2185 |
+
|
| 2186 |
+
metadata = {
|
| 2187 |
+
"video_id": video_id,
|
| 2188 |
+
"user_data": user_data,
|
| 2189 |
+
"content_subject": content_subject,
|
| 2190 |
+
"content_grade": content_grade,
|
| 2191 |
+
"socratic_mode": str(socratic_mode),
|
| 2192 |
+
"assistant_id": ai_name,
|
| 2193 |
+
"is_streaming": "false",
|
| 2194 |
+
}
|
| 2195 |
elif ai_type == "assistant":
|
| 2196 |
client = OPEN_AI_CLIENT
|
| 2197 |
assistant_id = OPEN_AI_ASSISTANT_ID_GPT4 #GPT 4 turbo
|
|
|
|
| 2224 |
chat_history = update_chat_history(user_message, response_text, chat_history)
|
| 2225 |
send_btn_update, send_feedback_btn_update = update_send_and_feedback_buttons(chat_history, CHAT_LIMIT)
|
| 2226 |
|
| 2227 |
+
user_id = user_data
|
| 2228 |
+
route = "chat_with_any_ai"
|
| 2229 |
+
endpoint = ai_type #chat_completions or assistant
|
| 2230 |
+
event_response = {
|
| 2231 |
+
"event_response": str(response_text),
|
| 2232 |
+
}
|
| 2233 |
+
event_response_json = json.dumps(event_response)
|
| 2234 |
+
prompt = {
|
| 2235 |
+
"thread_id": thread_id,
|
| 2236 |
+
"metadata": metadata,
|
| 2237 |
+
"user_message": user_message
|
| 2238 |
+
}
|
| 2239 |
+
prompt_json = json.dumps(prompt)
|
| 2240 |
+
|
| 2241 |
+
feature = "vaitor_chatbot"
|
| 2242 |
+
insert_log_to_bigquery(user_id, route, endpoint, event_response_json, prompt_json, feature)
|
| 2243 |
+
|
| 2244 |
# 返回聊天历史和空字符串清空输入框
|
| 2245 |
return "", chat_history, send_btn_update, send_feedback_btn_update, thread_id
|
| 2246 |
|
|
|
|
| 2254 |
"ai_client": GROQ_CLIENT,
|
| 2255 |
"ai_model_name": "groq_llama3",
|
| 2256 |
},
|
| 2257 |
+
# "lili": {
|
| 2258 |
+
# "ai_name": "lili",
|
| 2259 |
+
# "ai_client": BEDROCK_CLIENT,
|
| 2260 |
+
# "ai_model_name": "claude3",
|
| 2261 |
+
# },
|
| 2262 |
"lili": {
|
| 2263 |
"ai_name": "lili",
|
| 2264 |
+
"ai_client": GROQ_CLIENT,
|
| 2265 |
+
"ai_model_name": "groq_llama3",
|
| 2266 |
},
|
| 2267 |
"maimai": {
|
| 2268 |
"ai_name": "maimai",
|
|
|
|
| 2305 |
|
| 2306 |
return chatbot_config
|
| 2307 |
|
| 2308 |
+
def feedback_with_ai(user_data, ai_type, chat_history, thread_id=None):
|
| 2309 |
# prompt: 請依據以上的對話(chat_history),總結我的「提問力」,並給予我是否有「問對問題」的回饋和建議
|
| 2310 |
system_content = """
|
| 2311 |
你是一個擅長引導問答素養的老師,user 為學生的提問跟回答,請精讀對話過程,針對 user 給予回饋就好,根據以下 Rule:
|
|
|
|
| 2354 |
chat_history = update_chat_history(feedback_request_message, response_text, chat_history)
|
| 2355 |
feedback_btn_update = gr.update(value="已回饋", interactive=False, variant="secondary")
|
| 2356 |
|
| 2357 |
+
user_id = user_data
|
| 2358 |
+
route = "feedback_with_ai"
|
| 2359 |
+
endpoint = ai_type #chat_completions or assistant
|
| 2360 |
+
event_response = {
|
| 2361 |
+
"event_response": str(response_text),
|
| 2362 |
+
}
|
| 2363 |
+
event_response_json = json.dumps(event_response)
|
| 2364 |
+
prompt = {
|
| 2365 |
+
"thread_id": thread_id,
|
| 2366 |
+
"metadata": None,
|
| 2367 |
+
"user_message": user_content
|
| 2368 |
+
}
|
| 2369 |
+
prompt_json = json.dumps(prompt)
|
| 2370 |
+
feature = "vaitor_chatbot"
|
| 2371 |
+
insert_log_to_bigquery(user_id, route, endpoint, event_response_json, prompt_json, feature)
|
| 2372 |
+
|
| 2373 |
return chat_history, feedback_btn_update
|
| 2374 |
|
| 2375 |
def handle_conversation_by_open_ai_chat_completions(client, model_name, user_content, system_content):
|
|
|
|
| 2417 |
|
| 2418 |
if run_status == "completed":
|
| 2419 |
messages = client.beta.threads.messages.list(thread_id=thread.id)
|
| 2420 |
+
response = messages
|
| 2421 |
response_text = messages.data[0].content[0].text.value
|
| 2422 |
else:
|
| 2423 |
response_text = "學習精靈有點累,請稍後再試!"
|
|
|
|
| 2646 |
else:
|
| 2647 |
thread = client.beta.threads.retrieve(thread_id)
|
| 2648 |
print(f"old thread_id: {thread_id}")
|
| 2649 |
+
metadata = {
|
| 2650 |
+
"youtube_id": video_id,
|
| 2651 |
+
"user_data": user_data,
|
| 2652 |
+
"content_subject": content_subject,
|
| 2653 |
+
"content_grade": content_grade,
|
| 2654 |
+
"assistant_id": assistant_id,
|
| 2655 |
+
"is_streaming": "true",
|
| 2656 |
+
}
|
| 2657 |
client.beta.threads.update(
|
| 2658 |
thread_id=thread_id,
|
| 2659 |
+
metadata=metadata
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2660 |
)
|
| 2661 |
|
| 2662 |
# 向线程添加用户的消息
|
|
|
|
| 2681 |
except Exception as e:
|
| 2682 |
print(f"Error: {e}")
|
| 2683 |
raise gr.Error(f"Error: {e}")
|
| 2684 |
+
|
| 2685 |
+
user_id = user_data
|
| 2686 |
+
route = "chat_with_opan_ai_assistant_streaming"
|
| 2687 |
+
endpoint = "assistant_streaming"
|
| 2688 |
+
event_response = {
|
| 2689 |
+
"event_response": partial_messages
|
| 2690 |
+
}
|
| 2691 |
+
event_response_json = json.dumps(event_response)
|
| 2692 |
+
prompt = {
|
| 2693 |
+
"thread_id": thread_id,
|
| 2694 |
+
"metadata": metadata,
|
| 2695 |
+
"user_message": user_message
|
| 2696 |
+
}
|
| 2697 |
+
prompt_json = json.dumps(prompt)
|
| 2698 |
+
feature = "vaitor_chatbot"
|
| 2699 |
+
insert_log_to_bigquery(user_id, route, endpoint, event_response_json, prompt_json, feature)
|
| 2700 |
|
| 2701 |
def create_thread_id():
|
| 2702 |
thread = OPEN_AI_CLIENT.beta.threads.create()
|
|
|
|
| 2741 |
all_chatbot_select_btn_visible = gr.update(visible=False)
|
| 2742 |
return chatbot_select_accordion_visible, all_chatbot_select_btn_visible
|
| 2743 |
|
| 2744 |
+
def insert_log_to_bigquery(user_id, route, endpoint, event_response_json, prompt_json, feature):
|
| 2745 |
+
table_id = "junyiacademy.streaming_log.log_video_ai_usage"
|
| 2746 |
+
rows_to_insert = [
|
| 2747 |
+
{
|
| 2748 |
+
"user_id": user_id,
|
| 2749 |
+
"route": route,
|
| 2750 |
+
"endpoint": endpoint,
|
| 2751 |
+
"event_response": event_response_json,
|
| 2752 |
+
"event_timestamp": datetime.now(timezone.utc).isoformat(),
|
| 2753 |
+
"prompt": prompt_json,
|
| 2754 |
+
"feature": feature
|
| 2755 |
+
}
|
| 2756 |
+
]
|
| 2757 |
+
|
| 2758 |
+
errors = GBQ_CLIENT.insert_rows_json(table_id, rows_to_insert)
|
| 2759 |
+
if errors:
|
| 2760 |
+
print(f"Encountered errors while inserting rows: {errors}")
|
| 2761 |
+
else:
|
| 2762 |
+
print("Rows have been successfully inserted.")
|
| 2763 |
+
|
| 2764 |
|
| 2765 |
# --- Init params ---
|
| 2766 |
def init_params(text, request: gr.Request):
|
|
|
|
| 3056 |
worksheet_algorithm = gr.Dropdown(label="選擇教學策略或理論", choices=["Bloom認知階層理論", "Polya數學解題法", "CRA教學法"], value="Bloom認知階層理論", visible=False)
|
| 3057 |
worksheet_content_btn = gr.Button("生成學習單 📄", variant="primary", visible=True)
|
| 3058 |
with gr.Accordion("微調", open=False):
|
| 3059 |
+
worksheet_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
| 3060 |
+
worksheet_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
| 3061 |
+
worksheet_result_retrun_original = gr.Button("返回原始結果")
|
| 3062 |
with gr.Accordion("prompt", open=False) as worksheet_accordion:
|
| 3063 |
worksheet_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
| 3064 |
with gr.Column(scale=2):
|
| 3065 |
# 生成對應不同模式的結果
|
| 3066 |
+
worksheet_result_prompt = gr.Textbox(visible=False)
|
| 3067 |
+
worksheet_result_original = gr.Textbox(visible=False)
|
| 3068 |
+
worksheet_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
|
| 3069 |
+
worksheet_download_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
| 3070 |
+
worksheet_result_word_link = gr.File(label="Download Word")
|
|
|
|
| 3071 |
with gr.Tab("教案"):
|
| 3072 |
with gr.Row():
|
| 3073 |
with gr.Column(scale=1):
|
|
|
|
| 3076 |
lesson_plan_time = gr.Slider(label="選擇課程時間(分鐘)", minimum=10, maximum=120, step=5, value=40)
|
| 3077 |
lesson_plan_btn = gr.Button("生成教案 📕", variant="primary", visible=True)
|
| 3078 |
with gr.Accordion("微調", open=False):
|
| 3079 |
+
lesson_plan_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
| 3080 |
+
lesson_plan_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
| 3081 |
+
lesson_plan_result_retrun_original = gr.Button("返回原始結果")
|
| 3082 |
with gr.Accordion("prompt", open=False) as lesson_plan_accordion:
|
| 3083 |
lesson_plan_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
| 3084 |
with gr.Column(scale=2):
|
| 3085 |
# 生成對應不同模式的結果
|
| 3086 |
+
lesson_plan_result_prompt = gr.Textbox(visible=False)
|
| 3087 |
+
lesson_plan_result_original = gr.Textbox(visible=False)
|
| 3088 |
+
lesson_plan_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
|
| 3089 |
|
| 3090 |
+
lesson_plan_download_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
| 3091 |
+
lesson_plan_result_word_link = gr.File(label="Download Word")
|
| 3092 |
with gr.Tab("出場券"):
|
| 3093 |
with gr.Row():
|
| 3094 |
with gr.Column(scale=1):
|
|
|
|
| 3097 |
exit_ticket_time = gr.Slider(label="選擇出場券時間(分鐘)", minimum=5, maximum=10, step=1, value=8)
|
| 3098 |
exit_ticket_btn = gr.Button("生成出場券 🎟️", variant="primary", visible=True)
|
| 3099 |
with gr.Accordion("微調", open=False):
|
| 3100 |
+
exit_ticket_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
| 3101 |
+
exit_ticket_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
| 3102 |
+
exit_ticket_result_retrun_original = gr.Button("返回原始結果")
|
| 3103 |
with gr.Accordion("prompt", open=False) as exit_ticket_accordion:
|
| 3104 |
exit_ticket_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
| 3105 |
with gr.Column(scale=2):
|
| 3106 |
# 生成對應不同模式的結果
|
| 3107 |
+
exit_ticket_result_prompt = gr.Textbox(visible=False)
|
| 3108 |
+
exit_ticket_result_original = gr.Textbox(visible=False)
|
| 3109 |
+
exit_ticket_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
|
| 3110 |
|
| 3111 |
+
exit_ticket_download_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
| 3112 |
+
exit_ticket_result_word_link = gr.File(label="Download Word")
|
| 3113 |
|
| 3114 |
|
| 3115 |
# with gr.Tab("素養導向閱讀題組"):
|
|
|
|
| 3129 |
with gr.Accordion("See Details", open=False) as see_details:
|
| 3130 |
with gr.Row():
|
| 3131 |
is_env_prod = gr.Checkbox(value=False, label="is_env_prod")
|
| 3132 |
+
LLM_model = gr.Dropdown(label="LLM Model", choices=["open-ai-gpt-4o", "anthropic-claude-3-sonnet"], value="open-ai-gpt-4o", visible=True, interactive=True)
|
| 3133 |
with gr.Tab("逐字稿本文"):
|
| 3134 |
with gr.Row() as transcript_admmin:
|
| 3135 |
transcript_kind = gr.Textbox(value="transcript", show_label=False)
|
|
|
|
| 3320 |
)
|
| 3321 |
ai_send_feedback_btn.click(
|
| 3322 |
feedback_with_ai,
|
| 3323 |
+
inputs=[user_data, ai_chatbot_ai_type, ai_chatbot, ai_chatbot_thread_id],
|
| 3324 |
outputs=[ai_chatbot, ai_send_feedback_btn],
|
| 3325 |
scroll_to_output=True
|
| 3326 |
)
|
|
|
|
| 3657 |
{
|
| 3658 |
"button": worksheet_content_btn,
|
| 3659 |
"action": get_ai_content,
|
| 3660 |
+
"inputs": [password, user_data, video_id, df_string_output, content_subject, content_grade, content_level, worksheet_algorithm, worksheet_content_type_name],
|
| 3661 |
+
"outputs": [worksheet_result_original, worksheet_result, worksheet_prompt, worksheet_result_prompt]
|
| 3662 |
},
|
| 3663 |
{
|
| 3664 |
+
"button": worksheet_result_fine_tune_btn,
|
| 3665 |
+
"action": generate_ai_content_fine_tune_result,
|
| 3666 |
+
"inputs": [password, user_data, worksheet_result_prompt, df_string_output, worksheet_result, worksheet_result_fine_tune_prompt, worksheet_content_type_name],
|
| 3667 |
+
"outputs": [worksheet_result]
|
| 3668 |
},
|
| 3669 |
{
|
| 3670 |
+
"button": worksheet_download_button,
|
| 3671 |
"action": download_exam_result,
|
| 3672 |
+
"inputs": [worksheet_result],
|
| 3673 |
+
"outputs": [worksheet_result_word_link]
|
| 3674 |
},
|
| 3675 |
{
|
| 3676 |
+
"button": worksheet_result_retrun_original,
|
| 3677 |
"action": return_original_exam_result,
|
| 3678 |
+
"inputs": [worksheet_result_original],
|
| 3679 |
+
"outputs": [worksheet_result]
|
| 3680 |
},
|
| 3681 |
# 教案相關按鈕
|
| 3682 |
{
|
| 3683 |
"button": lesson_plan_btn,
|
| 3684 |
"action": get_ai_content,
|
| 3685 |
+
"inputs": [password, user_data, video_id, df_string_output, content_subject, content_grade, content_level, lesson_plan_time, lesson_plan_content_type_name],
|
| 3686 |
+
"outputs": [lesson_plan_result_original, lesson_plan_result, lesson_plan_prompt, lesson_plan_result_prompt]
|
| 3687 |
},
|
| 3688 |
{
|
| 3689 |
+
"button": lesson_plan_result_fine_tune_btn,
|
| 3690 |
+
"action": generate_ai_content_fine_tune_result,
|
| 3691 |
+
"inputs": [password, user_data, lesson_plan_result_prompt, df_string_output, lesson_plan_result, lesson_plan_result_fine_tune_prompt, lesson_plan_content_type_name],
|
| 3692 |
+
"outputs": [lesson_plan_result]
|
| 3693 |
},
|
| 3694 |
{
|
| 3695 |
+
"button": lesson_plan_download_button,
|
| 3696 |
"action": download_exam_result,
|
| 3697 |
+
"inputs": [lesson_plan_result],
|
| 3698 |
+
"outputs": [lesson_plan_result_word_link]
|
| 3699 |
},
|
| 3700 |
{
|
| 3701 |
+
"button": lesson_plan_result_retrun_original,
|
| 3702 |
"action": return_original_exam_result,
|
| 3703 |
+
"inputs": [lesson_plan_result_original],
|
| 3704 |
+
"outputs": [lesson_plan_result]
|
| 3705 |
},
|
| 3706 |
# 出場券相關按鈕
|
| 3707 |
{
|
| 3708 |
"button": exit_ticket_btn,
|
| 3709 |
"action": get_ai_content,
|
| 3710 |
+
"inputs": [password, user_data, video_id, df_string_output, content_subject, content_grade, content_level, exit_ticket_time, exit_ticket_content_type_name],
|
| 3711 |
+
"outputs": [exit_ticket_result_original, exit_ticket_result, exit_ticket_prompt, exit_ticket_result_prompt]
|
| 3712 |
},
|
| 3713 |
{
|
| 3714 |
+
"button": exit_ticket_result_fine_tune_btn,
|
| 3715 |
+
"action": generate_ai_content_fine_tune_result,
|
| 3716 |
+
"inputs": [password, user_data, exit_ticket_result_prompt, df_string_output, exit_ticket_result, exit_ticket_result_fine_tune_prompt, exit_ticket_content_type_name],
|
| 3717 |
+
"outputs": [exit_ticket_result]
|
| 3718 |
},
|
| 3719 |
{
|
| 3720 |
+
"button": exit_ticket_download_button,
|
| 3721 |
"action": download_exam_result,
|
| 3722 |
+
"inputs": [exit_ticket_result],
|
| 3723 |
+
"outputs": [exit_ticket_result_word_link]
|
| 3724 |
},
|
| 3725 |
{
|
| 3726 |
+
"button": exit_ticket_result_retrun_original,
|
| 3727 |
"action": return_original_exam_result,
|
| 3728 |
+
"inputs": [exit_ticket_result_original],
|
| 3729 |
+
"outputs": [exit_ticket_result]
|
| 3730 |
}
|
| 3731 |
]
|
| 3732 |
setup_education_buttons(education_buttons_config)
|
|
|
|
| 3753 |
outputs = init_outputs
|
| 3754 |
)
|
| 3755 |
|
| 3756 |
+
demo.launch(allowed_paths=["videos"], server_name="0.0.0.0", server_port=7860)
|
chatbot.py
CHANGED
|
@@ -87,7 +87,7 @@ class Chatbot:
|
|
| 87 |
"Content-Type": "application/json",
|
| 88 |
"x-api-key": self.jutor_chat_key,
|
| 89 |
}
|
| 90 |
-
model = "gpt-
|
| 91 |
print("======model======")
|
| 92 |
print(model)
|
| 93 |
# model = "gpt-3.5-turbo-0125"
|
|
|
|
| 87 |
"Content-Type": "application/json",
|
| 88 |
"x-api-key": self.jutor_chat_key,
|
| 89 |
}
|
| 90 |
+
model = "gpt-4o"
|
| 91 |
print("======model======")
|
| 92 |
print(model)
|
| 93 |
# model = "gpt-3.5-turbo-0125"
|
educational_material.py
CHANGED
|
@@ -99,7 +99,7 @@ class EducationalMaterial:
|
|
| 99 |
OPEN_AI_CLIENT = AI_Client
|
| 100 |
messages = [{"role": "system", "content": system_content}, {"role": "user", "content": user_content}]
|
| 101 |
request_payload = {
|
| 102 |
-
"model": "gpt-
|
| 103 |
"messages": messages,
|
| 104 |
"max_tokens": 4000,
|
| 105 |
"temperature": 0.9,
|
|
|
|
| 99 |
OPEN_AI_CLIENT = AI_Client
|
| 100 |
messages = [{"role": "system", "content": system_content}, {"role": "user", "content": user_content}]
|
| 101 |
request_payload = {
|
| 102 |
+
"model": "gpt-4o",
|
| 103 |
"messages": messages,
|
| 104 |
"max_tokens": 4000,
|
| 105 |
"temperature": 0.9,
|
requirements.txt
CHANGED
|
@@ -2,7 +2,6 @@ gradio==4.8.0
|
|
| 2 |
pandas
|
| 3 |
openai>=1.16.2
|
| 4 |
requests
|
| 5 |
-
beautifulsoup4
|
| 6 |
python-docx
|
| 7 |
youtube-transcript-api
|
| 8 |
moviepy
|
|
@@ -12,6 +11,7 @@ google-api-python-client
|
|
| 12 |
google-auth-httplib2
|
| 13 |
google-auth-oauthlib
|
| 14 |
google-cloud-storage
|
|
|
|
| 15 |
groq
|
| 16 |
yt_dlp
|
| 17 |
uuid
|
|
|
|
| 2 |
pandas
|
| 3 |
openai>=1.16.2
|
| 4 |
requests
|
|
|
|
| 5 |
python-docx
|
| 6 |
youtube-transcript-api
|
| 7 |
moviepy
|
|
|
|
| 11 |
google-auth-httplib2
|
| 12 |
google-auth-oauthlib
|
| 13 |
google-cloud-storage
|
| 14 |
+
google-cloud-bigquery
|
| 15 |
groq
|
| 16 |
yt_dlp
|
| 17 |
uuid
|