tsengiii commited on
Commit
b5e8566
·
verified ·
1 Parent(s): a2c600e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +14 -94
app.py CHANGED
@@ -1,5 +1,3 @@
1
- # app.py
2
-
3
  import os
4
  import openai
5
  import gradio as gr
@@ -12,55 +10,26 @@ from langchain_community.chat_models import ChatOpenAI
12
  import shutil # 用於文件複製
13
  import logging
14
 
15
- # 設置日誌配置
16
- logging.basicConfig(level=logging.INFO)
17
- logger = logging.getLogger(__name__)
18
-
19
  # 獲取 OpenAI API 密鑰(初始不使用固定密鑰)
20
  api_key_env = os.getenv("OPENAI_API_KEY")
21
  if api_key_env:
22
  openai.api_key = api_key_env
23
- logger.info("OpenAI API 密鑰已設置。")
24
  else:
25
- logger.info("未設置固定的 OpenAI API 密鑰。將使用使用者提供的密鑰。")
26
 
27
  # 確保向量資料庫目錄存在且有寫入權限
28
  VECTORDB_DIR = os.path.abspath("./data")
29
  os.makedirs(VECTORDB_DIR, exist_ok=True)
30
- os.chmod(VECTORDB_DIR, 0o755) # 設置適當的權限
31
- logger.info(f"VECTORDB_DIR set to: {VECTORDB_DIR}")
32
-
33
- # 定義測試 PDF 加載器的函數
34
- def test_pdf_loader(file_path, loader_type='PyMuPDFLoader'):
35
- logger.info(f"Testing PDF loader ({loader_type}) with file: {file_path}")
36
- try:
37
- if loader_type == 'PyMuPDFLoader':
38
- loader = PyMuPDFLoader(file_path)
39
- elif loader_type == 'PyPDFLoader':
40
- loader = PyPDFLoader(file_path)
41
- else:
42
- logger.error(f"Unknown loader type: {loader_type}")
43
- return
44
- loaded_docs = loader.load()
45
- if loaded_docs:
46
- logger.info(f"Successfully loaded {file_path} with {len(loaded_docs)} documents.")
47
- logger.info(f"Document content (first 500 chars): {loaded_docs[0].page_content[:500]}")
48
- else:
49
- logger.error(f"No documents loaded from {file_path}.")
50
- except Exception as e:
51
- logger.error(f"Error loading {file_path} with {loader_type}: {e}")
52
 
53
  # 定義載入和處理 PDF 文件的函數
54
  def load_and_process_documents(file_paths, loader_type='PyMuPDFLoader', api_key=None):
55
  if not api_key:
56
  raise ValueError("未提供 OpenAI API 密鑰。")
57
  documents = []
58
- logger.info("開始載入上傳的 PDF 文件。")
59
 
60
  for file_path in file_paths:
61
- logger.info(f"載入 PDF 文件: {file_path}")
62
  if not os.path.exists(file_path):
63
- logger.error(f"文件不存在: {file_path}")
64
  continue
65
  try:
66
  if loader_type == 'PyMuPDFLoader':
@@ -68,28 +37,19 @@ def load_and_process_documents(file_paths, loader_type='PyMuPDFLoader', api_key=
68
  elif loader_type == 'PyPDFLoader':
69
  loader = PyPDFLoader(file_path)
70
  else:
71
- logger.error(f"Unknown loader type: {loader_type}")
72
  continue
73
  loaded_docs = loader.load()
74
  if loaded_docs:
75
- logger.info(f"載入 {file_path} 成功,包含 {len(loaded_docs)} 個文檔。")
76
- # 打印第一個文檔的部分內容以確認
77
- logger.info(f"第一個文檔內容: {loaded_docs[0].page_content[:500]}")
78
  documents.extend(loaded_docs)
79
- else:
80
- logger.error(f"載入 {file_path} 但未找到任何文檔。")
81
  except Exception as e:
82
- logger.error(f"載入 {file_path} 時出現錯誤: {e}")
83
 
84
  if not documents:
85
  raise ValueError("沒有找到任何 PDF 文件或 PDF 文件無法載入。")
86
- else:
87
- logger.info(f"總共載入了 {len(documents)} 個文檔。")
88
 
89
  # 分割長文本
90
  text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=50)
91
  documents = text_splitter.split_documents(documents)
92
- logger.info(f"分割後的文檔數量: {len(documents)}")
93
 
94
  if not documents:
95
  raise ValueError("分割後的文檔列表為空。請檢查 PDF 文件內容。")
@@ -97,7 +57,6 @@ def load_and_process_documents(file_paths, loader_type='PyMuPDFLoader', api_key=
97
  # 初始化向量資料庫
98
  try:
99
  embeddings = OpenAIEmbeddings(openai_api_key=api_key) # 使用使用者的 API 密鑰
100
- logger.info("初始化 OpenAIEmbeddings 成功。")
101
  except Exception as e:
102
  raise ValueError(f"初始化 OpenAIEmbeddings 時出現錯誤: {e}")
103
 
@@ -107,7 +66,6 @@ def load_and_process_documents(file_paths, loader_type='PyMuPDFLoader', api_key=
107
  embedding=embeddings,
108
  persist_directory=VECTORDB_DIR
109
  )
110
- logger.info("初始化 Chroma 向量資料庫成功。")
111
  except Exception as e:
112
  raise ValueError(f"初始化 Chroma 向量資料庫時出現錯誤: {e}")
113
 
@@ -121,8 +79,8 @@ def handle_query(user_message, chat_history, vectordb, api_key):
121
 
122
  # 添加角色指令前綴
123
  preface = """
124
- 指令: 以繁體中文回答問題,200字以內。你是一位專業心理學家與調酒師,專精於 MBTI 人格與經典調酒主題。
125
- 非相關問題,請回應:「目前僅支援 MBTI 分析與經典調酒主題。」。
126
  """
127
  query = f"{preface} 查詢內容:{user_message}"
128
 
@@ -136,15 +94,13 @@ def handle_query(user_message, chat_history, vectordb, api_key):
136
  # 呼叫模型並處理查詢
137
  result = pdf_qa.invoke({"question": query, "chat_history": chat_history})
138
 
139
- # 檢查結果並更新聊天歷史
140
  if "answer" in result:
141
  chat_history = chat_history + [(user_message, result["answer"])]
142
  else:
143
- chat_history = chat_history + [(user_message, "抱歉,未能獲得有效回應。")]
144
  return chat_history
145
 
146
  except Exception as e:
147
- logger.error(f"Error in handle_query: {e}")
148
  return chat_history + [("系統", f"出現錯誤: {str(e)}")]
149
 
150
  # 定義保存 API 密鑰的函數
@@ -152,58 +108,27 @@ def save_api_key(api_key, state):
152
  if not api_key.startswith("sk-"):
153
  return "請輸入有效的 OpenAI API 密鑰。", state
154
  state['api_key'] = api_key
155
- logger.info("使用者已保存自己的 OpenAI API 密鑰。")
156
  return "API 密鑰已成功保存。您現在可以上傳 PDF 文件並開始提問。", state
157
 
158
  # 定義 Gradio 的處理函數
159
  def process_files(files, state):
160
- logger.info("process_files called")
161
  if files:
162
  try:
163
- # 檢查是否已保存 API 密鑰
164
  api_key = state.get('api_key', None)
165
  if not api_key:
166
- logger.error("使用者未提供 OpenAI API 密鑰。")
167
  return "請先在「設定 API 密鑰」標籤中輸入並保存您的 OpenAI API 密鑰。", state
168
 
169
- logger.info(f"Received {len(files)} files")
170
  saved_file_paths = []
171
  for idx, file_data in enumerate(files):
172
- # 為每個文件分配唯一的文件名
173
  filename = f"uploaded_{idx}.pdf"
174
  save_path = os.path.join(VECTORDB_DIR, filename)
175
  with open(save_path, "wb") as f:
176
  f.write(file_data)
177
- # 確認文件是否存在並檢查文件大小
178
- if os.path.exists(save_path):
179
- file_size = os.path.getsize(save_path)
180
- if file_size > 0:
181
- logger.info(f"File successfully saved to: {save_path} (Size: {file_size} bytes)")
182
- else:
183
- logger.error(f"File saved to {save_path} is empty.")
184
- raise ValueError(f"上傳的文件 {filename} 為空。")
185
- else:
186
- logger.error(f"Failed to save file to: {save_path}")
187
- raise FileNotFoundError(f"無法保存文件到 {save_path}")
188
  saved_file_paths.append(save_path)
189
- # 測試 PDF 加載器,先用 PyMuPDFLoader,再用 PyPDFLoader
190
- try:
191
- test_pdf_loader(save_path, loader_type='PyMuPDFLoader')
192
- except Exception as e:
193
- logger.error(f"PyMuPDFLoader failed: {e}")
194
- logger.info("Attempting to load with PyPDFLoader...")
195
- test_pdf_loader(save_path, loader_type='PyPDFLoader')
196
- # 列出 VECTORDB_DIR 中的所有文件
197
- saved_files = os.listdir(VECTORDB_DIR)
198
- logger.info(f"Files in VECTORDB_DIR ({VECTORDB_DIR}): {saved_files}")
199
- # 列出文件大小
200
- file_sizes = {file: os.path.getsize(os.path.join(VECTORDB_DIR, file)) for file in saved_files}
201
- logger.info(f"File sizes in VECTORDB_DIR: {file_sizes}")
202
  vectordb = load_and_process_documents(saved_file_paths, loader_type='PyMuPDFLoader', api_key=api_key)
203
  state['vectordb'] = vectordb
204
  return "PDF 文件已成功上傳並處理。您現在可以開始提問。", state
205
  except Exception as e:
206
- logger.error(f"Error in process_files: {e}")
207
  return f"處理文件時出現錯誤: {e}", state
208
  else:
209
  return "請上傳至少一個 PDF 文件。", state
@@ -216,15 +141,13 @@ def chat_interface(user_message, chat_history, state):
216
  if not api_key:
217
  return chat_history, state, "請先在「設定 API 密鑰」標籤中輸入並保存您的 OpenAI API 密鑰。"
218
 
219
- # 處理查詢
220
  updated_history = handle_query(user_message, chat_history, vectordb, api_key)
221
  return updated_history, state, ""
222
 
223
  # 設計 Gradio 介面
224
  with gr.Blocks() as demo:
225
- gr.Markdown("<h1 style='text-align: center;'>MBTI 與經典調酒 AI 助理</h1>")
226
 
227
- # 定義共享的 state
228
  state = gr.State({"vectordb": None, "api_key": None})
229
 
230
  with gr.Tab("設定 API 密鑰"):
@@ -239,50 +162,46 @@ with gr.Blocks() as demo:
239
  save_api_key_btn = gr.Button("保存 API 密鑰")
240
  api_key_status = gr.Textbox(label="狀態", interactive=False)
241
 
242
- with gr.Tab("上傳 PDF 文件"):
243
  with gr.Row():
244
  with gr.Column(scale=1):
245
  upload = gr.File(
246
  file_count="multiple",
247
  file_types=[".pdf"],
248
- label="上傳 PDF 文件",
249
  interactive=True,
250
- type="binary" # 修改為 'binary'
251
  )
252
  upload_btn = gr.Button("上傳並處理")
253
  upload_status = gr.Textbox(label="上傳狀態", interactive=False)
254
 
255
- with gr.Tab("聊天機器人"):
256
  chatbot = gr.Chatbot()
257
 
258
  with gr.Row():
259
  with gr.Column(scale=0.85):
260
- txt = gr.Textbox(show_label=False, placeholder="請輸入您的問題...")
261
  with gr.Column(scale=0.15, min_width=0):
262
  submit_btn = gr.Button("提問")
263
 
264
- # 綁定提問按鈕
265
  submit_btn.click(
266
  chat_interface,
267
  inputs=[txt, chatbot, state],
268
  outputs=[chatbot, state, txt]
269
  )
270
 
271
- # 綁定輸入框的提交事件
272
  txt.submit(
273
  chat_interface,
274
  inputs=[txt, chatbot, state],
275
  outputs=[chatbot, state, txt]
276
  )
277
 
278
- # 綁定保存 API 密鑰按鈕
279
  save_api_key_btn.click(
280
  save_api_key,
281
  inputs=[api_key_input, state],
282
  outputs=[api_key_status, state]
283
  )
284
 
285
- # 綁定上傳按鈕
286
  upload_btn.click(
287
  process_files,
288
  inputs=[upload, state],
@@ -290,4 +209,5 @@ with gr.Blocks() as demo:
290
  )
291
 
292
  # 啟動 Gradio 應用
293
- demo.launch()
 
 
 
 
1
  import os
2
  import openai
3
  import gradio as gr
 
10
  import shutil # 用於文件複製
11
  import logging
12
 
 
 
 
 
13
  # 獲取 OpenAI API 密鑰(初始不使用固定密鑰)
14
  api_key_env = os.getenv("OPENAI_API_KEY")
15
  if api_key_env:
16
  openai.api_key = api_key_env
 
17
  else:
18
+ print("未設置固定的 OpenAI API 密鑰。將使用使用者提供的密鑰。")
19
 
20
  # 確保向量資料庫目錄存在且有寫入權限
21
  VECTORDB_DIR = os.path.abspath("./data")
22
  os.makedirs(VECTORDB_DIR, exist_ok=True)
23
+ os.chmod(VECTORDB_DIR, 0o755)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
  # 定義載入和處理 PDF 文件的函數
26
  def load_and_process_documents(file_paths, loader_type='PyMuPDFLoader', api_key=None):
27
  if not api_key:
28
  raise ValueError("未提供 OpenAI API 密鑰。")
29
  documents = []
 
30
 
31
  for file_path in file_paths:
 
32
  if not os.path.exists(file_path):
 
33
  continue
34
  try:
35
  if loader_type == 'PyMuPDFLoader':
 
37
  elif loader_type == 'PyPDFLoader':
38
  loader = PyPDFLoader(file_path)
39
  else:
 
40
  continue
41
  loaded_docs = loader.load()
42
  if loaded_docs:
 
 
 
43
  documents.extend(loaded_docs)
 
 
44
  except Exception as e:
45
+ continue
46
 
47
  if not documents:
48
  raise ValueError("沒有找到任何 PDF 文件或 PDF 文件無法載入。")
 
 
49
 
50
  # 分割長文本
51
  text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=50)
52
  documents = text_splitter.split_documents(documents)
 
53
 
54
  if not documents:
55
  raise ValueError("分割後的文檔列表為空。請檢查 PDF 文件內容。")
 
57
  # 初始化向量資料庫
58
  try:
59
  embeddings = OpenAIEmbeddings(openai_api_key=api_key) # 使用使用者的 API 密鑰
 
60
  except Exception as e:
61
  raise ValueError(f"初始化 OpenAIEmbeddings 時出現錯誤: {e}")
62
 
 
66
  embedding=embeddings,
67
  persist_directory=VECTORDB_DIR
68
  )
 
69
  except Exception as e:
70
  raise ValueError(f"初始化 Chroma 向量資料庫時出現錯誤: {e}")
71
 
 
79
 
80
  # 添加角色指令前綴
81
  preface = """
82
+ 指令: 以繁體中文回答問題,200字以內。你是一位勞動法專家,針對員工權益與合同條款等法律問題進行回應。
83
+ 非相關問題,請回應:「目前僅支援勞動法相關問題。」。
84
  """
85
  query = f"{preface} 查詢內容:{user_message}"
86
 
 
94
  # 呼叫模型並處理查詢
95
  result = pdf_qa.invoke({"question": query, "chat_history": chat_history})
96
 
 
97
  if "answer" in result:
98
  chat_history = chat_history + [(user_message, result["answer"])]
99
  else:
100
+ chat_history = chat_history + [(user_message, "抱歉,未能獲得有效回應。")]
101
  return chat_history
102
 
103
  except Exception as e:
 
104
  return chat_history + [("系統", f"出現錯誤: {str(e)}")]
105
 
106
  # 定義保存 API 密鑰的函數
 
108
  if not api_key.startswith("sk-"):
109
  return "請輸入有效的 OpenAI API 密鑰。", state
110
  state['api_key'] = api_key
 
111
  return "API 密鑰已成功保存。您現在可以上傳 PDF 文件並開始提問。", state
112
 
113
  # 定義 Gradio 的處理函數
114
  def process_files(files, state):
 
115
  if files:
116
  try:
 
117
  api_key = state.get('api_key', None)
118
  if not api_key:
 
119
  return "請先在「設定 API 密鑰」標籤中輸入並保存您的 OpenAI API 密鑰。", state
120
 
 
121
  saved_file_paths = []
122
  for idx, file_data in enumerate(files):
 
123
  filename = f"uploaded_{idx}.pdf"
124
  save_path = os.path.join(VECTORDB_DIR, filename)
125
  with open(save_path, "wb") as f:
126
  f.write(file_data)
 
 
 
 
 
 
 
 
 
 
 
127
  saved_file_paths.append(save_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  vectordb = load_and_process_documents(saved_file_paths, loader_type='PyMuPDFLoader', api_key=api_key)
129
  state['vectordb'] = vectordb
130
  return "PDF 文件已成功上傳並處理。您現在可以開始提問。", state
131
  except Exception as e:
 
132
  return f"處理文件時出現錯誤: {e}", state
133
  else:
134
  return "請上傳至少一個 PDF 文件。", state
 
141
  if not api_key:
142
  return chat_history, state, "請先在「設定 API 密鑰」標籤中輸入並保存您的 OpenAI API 密鑰。"
143
 
 
144
  updated_history = handle_query(user_message, chat_history, vectordb, api_key)
145
  return updated_history, state, ""
146
 
147
  # 設計 Gradio 介面
148
  with gr.Blocks() as demo:
149
+ gr.Markdown("<h1 style='text-align: center;'>勞動法智能諮詢系統</h1>")
150
 
 
151
  state = gr.State({"vectordb": None, "api_key": None})
152
 
153
  with gr.Tab("設定 API 密鑰"):
 
162
  save_api_key_btn = gr.Button("保存 API 密鑰")
163
  api_key_status = gr.Textbox(label="狀態", interactive=False)
164
 
165
+ with gr.Tab("上傳勞動法 PDF 文件"):
166
  with gr.Row():
167
  with gr.Column(scale=1):
168
  upload = gr.File(
169
  file_count="multiple",
170
  file_types=[".pdf"],
171
+ label="上傳勞動法 PDF 文件",
172
  interactive=True,
173
+ type="binary"
174
  )
175
  upload_btn = gr.Button("上傳並處理")
176
  upload_status = gr.Textbox(label="上傳狀態", interactive=False)
177
 
178
+ with gr.Tab("智能諮詢"):
179
  chatbot = gr.Chatbot()
180
 
181
  with gr.Row():
182
  with gr.Column(scale=0.85):
183
+ txt = gr.Textbox(show_label=False, placeholder="請輸入您的法律問題...")
184
  with gr.Column(scale=0.15, min_width=0):
185
  submit_btn = gr.Button("提問")
186
 
 
187
  submit_btn.click(
188
  chat_interface,
189
  inputs=[txt, chatbot, state],
190
  outputs=[chatbot, state, txt]
191
  )
192
 
 
193
  txt.submit(
194
  chat_interface,
195
  inputs=[txt, chatbot, state],
196
  outputs=[chatbot, state, txt]
197
  )
198
 
 
199
  save_api_key_btn.click(
200
  save_api_key,
201
  inputs=[api_key_input, state],
202
  outputs=[api_key_status, state]
203
  )
204
 
 
205
  upload_btn.click(
206
  process_files,
207
  inputs=[upload, state],
 
209
  )
210
 
211
  # 啟動 Gradio 應用
212
+ demo.launch()
213
+