3a05chatgpt's picture
Update app.py
f73748f verified
raw
history blame
6.14 kB
import openai
import gradio as gr
import fitz # PyMuPDF
from openai import OpenAI
import traceback
# 全域變數
api_key = ""
selected_model = "gpt-4"
summary_text = ""
client = None
pdf_text = ""
def set_api_key(user_api_key):
"""設定 OpenAI API Key 並初始化客戶端"""
global api_key, client
try:
api_key = user_api_key.strip()
if not api_key:
return "❌ API Key 不能為空"
# 支援新舊 key 格式
if not (api_key.startswith('sk-') or api_key.startswith('sk-proj-')):
return "❌ API Key 格式錯誤,必須以 'sk-' 或 'sk-proj-' 開頭"
client = OpenAI(api_key=api_key)
# 測試 API Key 是否有效
test_response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "你好"}],
max_tokens=5
)
return "✅ API Key 已設定並驗證成功!"
except Exception as e:
if "incorrect_api_key" in str(e).lower():
return "❌ API Key 無效,請檢查是否正確"
elif "quota" in str(e).lower():
return "⚠️ API Key 有效,但配額不足"
else:
return f"❌ API Key 設定失敗: {str(e)}"
def set_model(model_name):
global selected_model
selected_model = model_name
return f"✅ 模型已選擇:{model_name}"
def extract_pdf_text(file_path):
try:
doc = fitz.open(file_path)
text = ""
for page_num, page in enumerate(doc):
page_text = page.get_text()
if page_text.strip():
text += f"\n--- 第 {page_num + 1} 頁 ---\n{page_text}"
doc.close()
return text
except Exception as e:
return f"❌ PDF 解析錯誤: {str(e)}"
def generate_summary(pdf_file):
global summary_text, pdf_text
if not client:
return "❌ 請先設定 OpenAI API Key"
if not pdf_file:
return "❌ 請先上傳 PDF 文件"
try:
pdf_text = extract_pdf_text(pdf_file.name)
if not pdf_text.strip():
return "⚠️ 無法解析 PDF 文字,可能為純圖片 PDF 或空白文件。"
pdf_text_truncated = pdf_text[:8000]
response = client.chat.completions.create(
model=selected_model,
messages=[
{"role": "system", "content": "請將以下 PDF 內容整理為條列式摘要,用繁體中文回答:"},
{"role": "user", "content": pdf_text_truncated}
],
temperature=0.3
)
summary_text = response.choices[0].message.content
return summary_text
except Exception as e:
print(traceback.format_exc())
return f"❌ 摘要生成失敗: {str(e)}"
def ask_question(user_question):
if not client:
return "❌ 請先設定 OpenAI API Key"
if not summary_text and not pdf_text:
return "❌ 請先生成 PDF 摘要"
if not user_question.strip():
return "❌ 請輸入問題"
try:
context = f"PDF 摘要:\n{summary_text}\n\n原始內容(部分):\n{pdf_text[:2000]}"
response = client.chat.completions.create(
model=selected_model,
messages=[
{"role": "system", "content": f"根據以下 PDF 內容回答問題,請用繁體中文回答:\n{context}"},
{"role": "user", "content": user_question}
],
temperature=0.2
)
return response.choices[0].message.content
except Exception as e:
print(traceback.format_exc())
return f"❌ 問答生成失敗: {str(e)}"
def clear_all():
global summary_text, pdf_text
summary_text = ""
pdf_text = ""
return "", "", ""
with gr.Blocks(
title="PDF 摘要助手",
css="""
.gradio-container {
max-width: none !important;
width: 100% !important;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
min-height: 100vh;
}
.main-content {
max-width: 1600px !important;
margin: 20px auto !important;
padding: 30px !important;
background: rgba(255, 255, 255, 0.95) !important;
border-radius: 20px !important;
}
"""
) as demo:
with gr.Column():
gr.Markdown("## 📄 PDF 摘要 & 問答助手")
with gr.Tab("🔧 設定"):
api_key_input = gr.Textbox(label="🔑 輸入 OpenAI API Key", type="password")
api_key_status = gr.Textbox(label="API 狀態", interactive=False, value="等待設定 API Key...")
api_key_btn = gr.Button("確認 API Key")
api_key_btn.click(set_api_key, inputs=api_key_input, outputs=api_key_status)
model_choice = gr.Radio(["gpt-4", "gpt-4.1", "gpt-4.5"], label="選擇 AI 模型", value="gpt-4")
model_status = gr.Textbox(label="模型狀態", interactive=False, value="✅ 已選擇:gpt-4")
model_choice.change(set_model, inputs=model_choice, outputs=model_status)
with gr.Tab("📄 摘要"):
pdf_upload = gr.File(label="上傳 PDF", file_types=[".pdf"])
summary_btn = gr.Button("生成摘要")
summary_output = gr.Textbox(label="PDF 摘要", lines=12)
summary_btn.click(generate_summary, inputs=pdf_upload, outputs=summary_output)
with gr.Tab("❓ 問答"):
question_input = gr.Textbox(label="請輸入問題", lines=2)
question_btn = gr.Button("送出問題")
answer_output = gr.Textbox(label="AI 回答", lines=8)
question_btn.click(ask_question, inputs=question_input, outputs=answer_output)
question_input.submit(ask_question, inputs=question_input, outputs=answer_output)
clear_btn = gr.Button("🗑️ 清除所有資料")
clear_btn.click(clear_all, outputs=[summary_output, question_input, answer_output])
if __name__ == "__main__":
demo.launch(
show_error=True,
share=True,
server_name="0.0.0.0",
server_port=7860
)