NBLM / app.py
youngtsai's picture
password
e485414
raw
history blame
11 kB
# -*- coding: utf-8 -*-
from typing import Container
from config.config import PASSWORD
import gradio as gr
import os
import shutil
import tempfile
from google import genai
from google.genai import types
from initializer import initialize_clients, initialize_password
# 初始化 Google Cloud Storage 服務和 GENAI 客戶端
GCS_SERVICE, GENAI_CLIENT = initialize_clients()
GCS_CLIENT = GCS_SERVICE.client
PASSWORD = initialize_password()
def toggle_visibility(toggle_value):
return gr.update(visible=toggle_value)
def mock_question_answer(question, history):
# 假資料模擬回答
answers = {
"文件的核心觀點是什麼?": "這份文件的核心觀點是關於人工智慧如何提升工作效率。",
"有哪些關鍵詞或數據?": "關鍵詞包括:人工智慧、工作效率、數據分析。",
"文件的摘要是什麼?": "這份文件討論了如何利用人工智慧工具,提升企業的運營效率和決策速度。"
}
response = answers.get(question, "抱歉,我無法回答這個問題。請嘗試其他問題!")
history.append({"role": "user", "content": question})
history.append({"role": "assistant", "content": response})
return history, ""
def mock_summary():
# 假資料模擬摘要
return "這份文件主要討論人工智慧在工作效率提升方面的應用,並提供了實際案例來說明其價值。"
def add_to_file_list(file, file_list):
if file:
temp_dir = tempfile.gettempdir()
temp_path = os.path.join(temp_dir, os.path.basename(file.name))
shutil.copy(file.name, temp_path) # 將文件存儲到臨時目錄
file_list.append(temp_path)
display_list = [os.path.basename(path) if os.path.basename(path) else path for path in file_list]
return gr.update(choices=display_list), None
def add_youtube_to_list(youtube_link, file_list):
if youtube_link:
file_list.append(youtube_link)
display_list = [os.path.basename(path) if os.path.basename(path) else path for path in file_list]
return gr.update(choices=display_list), ""
def generate_transcript(youtube_link):
print(f"\n開始生成 YouTube 逐字稿: {youtube_link}")
try:
print("初始化 Gemini 模型設定...")
video = types.Part.from_uri(
file_uri=youtube_link,
mime_type="video/*",
)
model = "gemini-2.0-flash-exp"
contents = [
types.Content(
role="user",
parts=[
video,
types.Part.from_text("""請給我帶時間軸的逐字稿,請統一用 zhTW語言""")
]
)
]
generate_content_config = types.GenerateContentConfig(
temperature=1,
top_p=0.95,
max_output_tokens=8192,
response_modalities=["TEXT"],
safety_settings=[
types.SafetySetting(category="HARM_CATEGORY_HATE_SPEECH", threshold="OFF"),
types.SafetySetting(category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="OFF"),
types.SafetySetting(category="HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold="OFF"),
types.SafetySetting(category="HARM_CATEGORY_HARASSMENT", threshold="OFF")
],
)
print("開始串流生成逐字稿...")
transcript_text = ""
for chunk in GENAI_CLIENT.models.generate_content_stream(
model=model,
contents=contents,
config=generate_content_config,
):
# Extract only text content from candidates
if hasattr(chunk, 'candidates') and chunk.candidates:
for candidate in chunk.candidates:
if (hasattr(candidate, 'content') and
hasattr(candidate.content, 'parts')):
for part in candidate.content.parts:
if hasattr(part, 'text') and part.text:
transcript_text += part.text
print(".", end="", flush=True)
print("\n逐字稿生成完成!")
return transcript_text
except Exception as e:
print(f"\n生成逐字稿時發生錯誤: {str(e)}")
raise
def generate_summary(transcript):
"""Generate a summary from the transcript using Gemini."""
try:
print("\n開始生成摘要...")
model = "gemini-2.0-flash-exp"
contents = [
types.Content(
role="user",
parts=[
types.Part.from_text(
f"""請根據以下逐字稿生成重點摘要,以條列方式呈現主要觀點:
{transcript}
請以下列格式輸出:
# 主要觀點:
1. [重點1]
2. [重點2]
...
# 結論:
[整體結論]
"""
)
]
)
]
response = GENAI_CLIENT.models.generate_content(
model=model,
contents=contents,
)
print("摘要生成完成!")
return response.text
except Exception as e:
print(f"\n生成摘要時發生錯誤: {str(e)}")
raise
def process_all_files(file_list):
print("\n=== 開始處理檔案 ===")
print(f"待處理檔案數量: {len(file_list)}")
result_text = ""
transcript_text = ""
for index, file in enumerate(file_list, 1):
print(f"\n處理第 {index}/{len(file_list)} 個檔案: {file}")
if "youtube.com" in file or "youtu.be" in file:
print(f"檢測到 YouTube 連結,開始生成逐字稿...")
try:
transcript = generate_transcript(file)
print("✓ YouTube 逐字稿生成成功")
result_text += f"🟢 YouTube 影片處理完成: {file}\n"
transcript_text += f"\n=== {file} 的逐字稿 ===\n{transcript}\n"
except Exception as e:
print(f"✗ YouTube 逐字稿生成失敗: {str(e)}")
result_text += f"🔴 YouTube 影片處理失敗: {file}\n"
else:
print(f"處理一般檔案: {file}")
try:
# 這裡可以加入其他檔案的處理邏輯
print("✓ 檔案處理成功")
result_text += f"🟢 檔案處理完成: {file}\n"
except Exception as e:
print(f"✗ 檔案處理失敗: {str(e)}")
result_text += f"🔴 檔案處理失敗: {file}\n"
print("\n=== 檔案處理完成 ===")
return result_text, transcript_text
def process_with_auth(password, file_list):
"""包含密碼驗證的處理函數"""
if not password or password != PASSWORD:
return "請輸入正確的密碼", "", gr.update(visible=False)
result_text, transcript_text = process_all_files(file_list)
return result_text, transcript_text
def on_summary_click(transcript):
if not transcript:
return "請先上傳文件或輸入 YouTube 連結並處理完成後再生成摘要。"
summary = generate_summary(transcript)
return summary
with gr.Blocks() as demo:
with gr.Row():
gr.Markdown("# AI Notes Assistant")
password_input = gr.Textbox(label="password")
with gr.Row():
source_toggle = gr.Checkbox(label="顯示來源選單", value=True)
chat_toggle = gr.Checkbox(label="顯示對話區域", value=True)
feature_toggle = gr.Checkbox(label="顯示功能卡片", value=True)
with gr.Row():
with gr.Column(visible=True) as source_column:
gr.Markdown("### 來源選單")
file_list = gr.State([])
with gr.Tab("YouTube 連結"):
youtube_link = gr.Textbox(label="輸入 YouTube 連結")
add_youtube_button = gr.Button("添加到來源列表")
add_youtube_button.click(add_youtube_to_list, inputs=[youtube_link, file_list], outputs=[file_list, youtube_link])
with gr.Tab("上傳檔案"):
upload_file = gr.File(label="從電腦添加文件", file_types=[".txt", ".pdf", ".docx"])
add_file_button = gr.Button("添加到來源列表")
add_file_button.click(add_to_file_list, inputs=[upload_file, file_list], outputs=[file_list, upload_file])
file_display = gr.CheckboxGroup(label="已上傳的文件", interactive=True)
process_files_button = gr.Button("處理檔案")
rag_result = gr.Textbox(label="處理狀態", interactive=False)
file_list.change(lambda x: gr.update(choices = [os.path.basename(path) if os.path.basename(path) else path for path in x]), inputs=file_list, outputs=file_display)
with gr.Column(visible=True) as chat_column:
gr.Markdown("### 對話區域")
chatbot = gr.Chatbot(label="聊天記錄", type="messages")
question = gr.Textbox(label="輸入問題,例如:文件的核心觀點是什麼?")
ask_button = gr.Button("提問")
with gr.Column(visible=True) as feature_column:
gr.Markdown("### 功能卡片")
with gr.Tab("摘要生成"):
summary_button = gr.Button("生成摘要", visible=False)
summary_output = gr.Markdown(
label="摘要",
show_label=True,
show_copy_button=True,
container=True
)
with gr.Tab("逐字稿"):
transcript_display = gr.Textbox(
label="YouTube 逐字稿",
interactive=False,
lines=10,
show_copy_button=True,
placeholder="處理 YouTube 影片後,逐字稿將顯示在這裡..."
)
with gr.Tab("其他功能"):
gr.Markdown("此處可以添加更多功能卡片")
source_toggle.change(toggle_visibility, inputs=source_toggle, outputs=source_column)
chat_toggle.change(toggle_visibility, inputs=chat_toggle, outputs=chat_column)
feature_toggle.change(toggle_visibility, inputs=feature_toggle, outputs=feature_column)
# 更新處理檔案按鈕的事件處理
process_files_button.click(
fn=process_with_auth,
inputs=[password_input, file_list],
outputs=[
rag_result,
transcript_display
]
).then(
fn=on_summary_click,
inputs=[transcript_display],
outputs=[summary_output]
)
history = gr.State([])
ask_button.click(mock_question_answer, inputs=[question, history], outputs=[chatbot, question])
summary_button.click(
fn=on_summary_click,
inputs=[transcript_display],
outputs=[summary_output]
)
demo.launch(share=True)