3a05chatgpt commited on
Commit
0c8d1ca
·
verified ·
1 Parent(s): c0edcf5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -348
app.py CHANGED
@@ -2,11 +2,8 @@ import openai
2
  import gradio as gr
3
  import fitz # PyMuPDF
4
  from openai import OpenAI
5
- import os
6
- import tempfile
7
  import traceback
8
 
9
- # 全域變數
10
  api_key = ""
11
  selected_model = "gpt-4"
12
  summary_text = ""
@@ -14,445 +11,142 @@ client = None
14
  pdf_text = ""
15
 
16
  def set_api_key(user_api_key):
17
- """設定 OpenAI API Key 並初始化客戶端"""
18
  global api_key, client
19
  try:
20
  api_key = user_api_key.strip()
21
  if not api_key:
22
  return "❌ API Key 不能為空"
23
-
24
  client = OpenAI(api_key=api_key)
25
-
26
- # 測試 API Key 是否有效
27
- test_response = client.chat.completions.create(
28
  model="gpt-4",
29
  messages=[{"role": "user", "content": "你好"}],
30
  max_tokens=5
31
  )
32
-
33
  return "✅ API Key 已設定並驗證成功"
34
  except Exception as e:
35
  return f"❌ API Key 設定失敗: {str(e)}"
36
 
37
  def set_model(model_name):
38
- """設定選擇的模型"""
39
  global selected_model
40
  selected_model = model_name
41
  return f"✅ 模型已選擇:{model_name}"
42
 
43
  def extract_pdf_text(file_path):
44
- """從 PDF 文件中提取文字"""
45
  try:
46
  doc = fitz.open(file_path)
47
  text = ""
48
  for page_num, page in enumerate(doc):
49
  page_text = page.get_text()
50
- if page_text.strip(): # 只添加非空白頁面
51
- text += f"\n--- 第 {page_num + 1} 頁 ---\n"
52
- text += page_text
53
  doc.close()
54
  return text
55
  except Exception as e:
56
  return f"❌ PDF 解析錯誤: {str(e)}"
57
 
58
  def generate_summary(pdf_file):
59
- """從 PDF 內容生成摘要"""
60
  global summary_text, pdf_text
61
-
62
  if not client:
63
  return "❌ 請先設定 OpenAI API Key"
64
-
65
  if not pdf_file:
66
  return "❌ 請先上傳 PDF 文件"
67
-
68
  try:
69
- # 從 PDF 提取文字
70
  pdf_text = extract_pdf_text(pdf_file.name)
71
-
72
  if not pdf_text.strip():
73
  return "⚠️ 無法解析 PDF 文字,可能為純圖片 PDF 或空白文件。"
74
-
75
- # 檢查文字長度,必要時截斷
76
- max_chars = 8000 # 為系統提示留出空間
77
- if len(pdf_text) > max_chars:
78
- pdf_text_truncated = pdf_text[:max_chars] + "\n\n[文本已截斷,僅顯示前 8000 字符]"
79
- else:
80
- pdf_text_truncated = pdf_text
81
-
82
- # 生成摘要
83
  response = client.chat.completions.create(
84
  model=selected_model,
85
  messages=[
86
- {
87
- "role": "system",
88
- "content": """你是一個專業的文檔摘要助手。請將以下 PDF 內容整理為結構化的摘要:
89
-
90
- 1. 首先提供一個簡短的總體概述
91
- 2. 然後按照重要性列出主要重點(使用項目符號)
92
- 3. 如果有數據或統計信息,請特別標注
93
- 4. 如果有結論或建議,請單獨列出
94
-
95
- 請用繁體中文回答,保持專業且易於理解的語調。"""
96
- },
97
  {"role": "user", "content": pdf_text_truncated}
98
  ],
99
  temperature=0.3
100
  )
101
-
102
  summary_text = response.choices[0].message.content
103
  return summary_text
104
-
105
  except Exception as e:
106
- error_msg = f"❌ 摘要生成失敗: {str(e)}"
107
- print(f"錯誤詳情: {traceback.format_exc()}")
108
- return error_msg
109
 
110
  def ask_question(user_question):
111
- """基於 PDF 內容回答問題"""
112
  if not client:
113
  return "❌ 請先設定 OpenAI API Key"
114
-
115
  if not summary_text and not pdf_text:
116
  return "❌ 請先生成 PDF 摘要"
117
-
118
  if not user_question.strip():
119
  return "❌ 請輸入問題"
120
-
121
  try:
122
- # 使用摘要和原始文本來提供更好的上下文
123
  context = f"PDF 摘要:\n{summary_text}\n\n原始內容(部分):\n{pdf_text[:2000]}"
124
-
125
  response = client.chat.completions.create(
126
  model=selected_model,
127
  messages=[
128
- {
129
- "role": "system",
130
- "content": f"""你是一個專業的文檔問答助手。請基於提供的 PDF 內容回答用戶問題。
131
-
132
- 規則:
133
- 1. 只根據提供的文檔內容回答
134
- 2. 如果文檔中沒有相關信息,請明確說明
135
- 3. 引用具體的文檔內容來支持你的回答
136
- 4. 用繁體中文回答
137
- 5. 保持客觀和準確
138
-
139
- 文檔內容:
140
- {context}"""
141
- },
142
  {"role": "user", "content": user_question}
143
  ],
144
  temperature=0.2
145
  )
146
-
147
  return response.choices[0].message.content
148
-
149
  except Exception as e:
150
- error_msg = f"❌ 問答生成失敗: {str(e)}"
151
- print(f"錯誤詳情: {traceback.format_exc()}")
152
- return error_msg
153
 
154
  def clear_all():
155
- """清除所有資料"""
156
  global summary_text, pdf_text
157
  summary_text = ""
158
  pdf_text = ""
159
  return "", "", ""
160
 
161
- # 創建 Gradio 介面 - 全螢幕版本
162
  with gr.Blocks(
163
  theme=gr.themes.Soft(),
164
  title="PDF 摘要助手",
165
  css="""
166
- /* 全螢幕樣式 */
167
  .gradio-container {
168
  max-width: none !important;
169
- width: 100vw !important;
170
- height: 100vh !important;
171
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
172
- margin: 0 !important;
173
- padding: 0 !important;
174
  }
175
-
176
- /* 主要內容區域 - 全螢幕 */
177
  .main-content {
178
  background: rgba(255, 255, 255, 0.95) !important;
179
  border-radius: 20px !important;
180
- margin: 15px !important;
181
  padding: 30px !important;
 
182
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important;
183
  backdrop-filter: blur(10px) !important;
184
- width: calc(100vw - 30px) !important;
185
- min-height: calc(100vh - 30px) !important;
186
- box-sizing: border-box !important;
187
- }
188
-
189
- /* 標題樣式 */
190
- .main-header {
191
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
192
- -webkit-background-clip: text !important;
193
- -webkit-text-fill-color: transparent !important;
194
- text-align: center !important;
195
- font-size: 2.8em !important;
196
- font-weight: bold !important;
197
- margin-bottom: 25px !important;
198
- }
199
-
200
- /* 分頁導航 */
201
- .gradio-tabs {
202
- border-radius: 15px !important;
203
- overflow: hidden !important;
204
- width: 100% !important;
205
- }
206
-
207
- .gradio-tabitem {
208
- padding: 30px !important;
209
- background: white !important;
210
- border-radius: 0 0 15px 15px !important;
211
- width: 100% !important;
212
- }
213
-
214
- /* 輸入框樣式 */
215
- input[type="text"],
216
- input[type="password"],
217
- textarea {
218
- border: 2px solid #e0e6ff !important;
219
- border-radius: 12px !important;
220
- padding: 15px !important;
221
- font-size: 16px !important;
222
- transition: all 0.3s ease !important;
223
- width: 100% !important;
224
- box-sizing: border-box !important;
225
- }
226
-
227
- input[type="text"]:focus,
228
- input[type="password"]:focus,
229
- textarea:focus {
230
- border-color: #667eea !important;
231
- box-shadow: 0 0 20px rgba(102, 126, 234, 0.3) !important;
232
- outline: none !important;
233
- }
234
-
235
- /* 按鈕樣式 */
236
- button {
237
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
238
- border: none !important;
239
- border-radius: 12px !important;
240
- color: white !important;
241
- font-weight: 600 !important;
242
- padding: 15px 30px !important;
243
- font-size: 16px !important;
244
- transition: all 0.3s ease !important;
245
- box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4) !important;
246
- cursor: pointer !important;
247
- min-height: 50px !important;
248
- margin: 8px 4px !important;
249
- }
250
-
251
- button:hover {
252
- transform: translateY(-2px) !important;
253
- box-shadow: 0 8px 25px rgba(102, 126, 234, 0.6) !important;
254
- }
255
-
256
- /* 次要按鈕 */
257
- button[variant="secondary"] {
258
- background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%) !important;
259
- }
260
-
261
- button[variant="secondary"]:hover {
262
- background: linear-gradient(135deg, #e081e9 0%, #e3455a 100%) !important;
263
- }
264
-
265
- /* 文件上傳區域 */
266
- .file-upload {
267
- border: 3px dashed #667eea !important;
268
- border-radius: 15px !important;
269
- background: rgba(102, 126, 234, 0.05) !important;
270
- padding: 40px !important;
271
- text-align: center !important;
272
- transition: all 0.3s ease !important;
273
- min-height: 120px !important;
274
- width: 100% !important;
275
- }
276
-
277
- .file-upload:hover {
278
- background: rgba(102, 126, 234, 0.1) !important;
279
- border-color: #764ba2 !important;
280
- }
281
-
282
- /* 行和列的布局 */
283
- .gr-row {
284
- display: flex !important;
285
- gap: 20px !important;
286
- width: 100% !important;
287
- margin: 15px 0 !important;
288
- }
289
-
290
- .gr-column {
291
- flex: 1 !important;
292
- min-width: 0 !important;
293
- }
294
-
295
- /* 文字輸出區域 */
296
- .gr-textbox {
297
- width: 100% !important;
298
- }
299
-
300
- /* 隱藏所有 footer 和 logo */
301
- footer,
302
- .gradio-container footer,
303
- div[class*="footer"],
304
- div[class*="Footer"],
305
- .gr-footer,
306
- .gradio-footer {
307
- display: none !important;
308
- }
309
-
310
- /* 響應式設計 */
311
- @media (max-width: 768px) {
312
- .main-content {
313
- margin: 5px !important;
314
- padding: 15px !important;
315
- width: calc(100vw - 10px) !important;
316
- }
317
-
318
- .main-header {
319
- font-size: 2em !important;
320
- }
321
-
322
- .gr-row {
323
- flex-direction: column !important;
324
- }
325
-
326
- button {
327
- width: 100% !important;
328
- margin: 5px 0 !important;
329
- }
330
  }
331
  """
332
  ) as demo:
333
  with gr.Column(elem_classes="main-content"):
334
- gr.HTML("""
335
- <div class="main-header">📄 PDF 摘要 & 問答助手</div>
336
-
337
- <div style="text-align: center; margin-bottom: 30px; padding: 25px; background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); border-radius: 15px; border-left: 5px solid #667eea;">
338
- <h3 style="color: #667eea; margin-bottom: 15px;">🚀 歡迎使用 PDF 智能分析工具!</h3>
339
- <div style="display: flex; justify-content: space-around; flex-wrap: wrap; margin: 20px 0;">
340
- <div style="margin: 10px; padding: 15px; background: white; border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1); min-width: 200px; flex: 1; max-width: 300px;">
341
- <div style="font-size: 24px; margin-bottom: 10px;">📋</div>
342
- <strong>智能摘要生成</strong><br>
343
- <span style="color: #666;">自動分析 PDF 內容並生成結構化摘要</span>
344
- </div>
345
- <div style="margin: 10px; padding: 15px; background: white; border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1); min-width: 200px; flex: 1; max-width: 300px;">
346
- <div style="font-size: 24px; margin-bottom: 10px;">🤖</div>
347
- <strong>AI 問答系統</strong><br>
348
- <span style="color: #666;">基於文檔內容回答您的問題</span>
349
- </div>
350
- <div style="margin: 10px; padding: 15px; background: white; border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1); min-width: 200px; flex: 1; max-width: 300px;">
351
- <div style="font-size: 24px; margin-bottom: 10px;">💡</div>
352
- <strong>快速理解</strong><br>
353
- <span style="color: #666;">快速掌握長篇文檔的核心內容</span>
354
- </div>
355
- </div>
356
- <div style="background: rgba(255, 193, 7, 0.1); padding: 15px; border-radius: 10px; border-left: 4px solid #ffc107; margin-top: 20px;">
357
- <strong style="color: #e65100;">⚠️ 重要提醒:</strong> 使用前請先在「🔧 設定」頁面輸入您的 OpenAI API Key
358
- </div>
359
- </div>
360
- """)
361
-
362
  with gr.Tab("🔧 設定"):
363
- gr.Markdown("### ⚙️ 基本設定")
364
- with gr.Row():
365
- with gr.Column():
366
- api_key_input = gr.Textbox(
367
- label="🔑 輸入 OpenAI API Key",
368
- type="password",
369
- placeholder="請輸入您的 OpenAI API Key (sk-...)"
370
- )
371
- api_key_status = gr.Textbox(
372
- label="📊 API 連接狀態",
373
- interactive=False,
374
- value="🔄 等待設定 API Key..."
375
- )
376
-
377
- with gr.Column():
378
- model_choice = gr.Radio(
379
- ["gpt-4", "gpt-4.1", "gpt-4.5"],
380
- label="🤖 選擇 AI 模型",
381
- value="gpt-4"
382
- )
383
- model_status = gr.Textbox(
384
- label="🎯 當前模型",
385
- interactive=False,
386
- value="✅ 已選擇:gpt-4"
387
- )
388
-
389
- with gr.Tab("📄 PDF 處理"):
390
- gr.Markdown("### 📁 文件上傳與摘要生成")
391
- with gr.Row():
392
- with gr.Column():
393
- pdf_upload = gr.File(
394
- label="📎 選擇 PDF 文件",
395
- file_types=[".pdf"]
396
- )
397
- gr.Markdown("**支援格式**:PDF 文件 (建議 < 10MB)")
398
-
399
- with gr.Row():
400
- summary_btn = gr.Button(
401
- "🔄 開始生成摘要",
402
- variant="primary"
403
- )
404
- clear_btn = gr.Button(
405
- "🗑️ 清除所有資料",
406
- variant="secondary"
407
- )
408
-
409
- with gr.Column():
410
- summary_output = gr.Textbox(
411
- label="📋 AI 生成的文檔摘要",
412
- lines=20,
413
- placeholder="📄 上傳 PDF 文件並點擊「開始生成摘要」按鈕,AI 將為您分析文檔內容..."
414
- )
415
-
416
- with gr.Tab("❓ 智能問答"):
417
- gr.Markdown("### 💬 基於文檔內容的 AI 問答")
418
- with gr.Row():
419
- with gr.Column():
420
- question_input = gr.Textbox(
421
- label="💭 請輸入您的問題",
422
- placeholder="例如:這份文件的主要結論是什麼?文中提到的關鍵數據有哪些?",
423
- lines=4
424
- )
425
- question_btn = gr.Button(
426
- "📤 發送問題",
427
- variant="primary"
428
- )
429
-
430
- gr.Markdown("""
431
- **💡 問題範例:**
432
- - 這份文件討論的主要議題是什麼?
433
- - 文中有哪些重要的統計數據?
434
- - 作者的主要觀點和結論是什麼?
435
- - 文件中提到的建議有哪些?
436
- """)
437
-
438
- with gr.Column():
439
- answer_output = gr.Textbox(
440
- label="🤖 AI 智能回答",
441
- lines=18,
442
- placeholder="🤖 請先上傳並生成 PDF 摘要,然後在左側輸入問題,AI 將基於文檔內容為您提供詳細回答..."
443
- )
444
-
445
- # 事件處理器
446
- api_key_input.submit(set_api_key, inputs=api_key_input, outputs=api_key_status)
447
- model_choice.change(set_model, inputs=model_choice, outputs=model_status)
448
- summary_btn.click(generate_summary, inputs=pdf_upload, outputs=summary_output)
449
- question_btn.click(ask_question, inputs=question_input, outputs=answer_output)
450
- question_input.submit(ask_question, inputs=question_input, outputs=answer_output)
451
- clear_btn.click(clear_all, outputs=[summary_output, question_input, answer_output])
452
 
453
  if __name__ == "__main__":
454
- demo.launch(
455
- share=False,
456
- show_api=False,
457
- show_error=True
458
- )
 
2
  import gradio as gr
3
  import fitz # PyMuPDF
4
  from openai import OpenAI
 
 
5
  import traceback
6
 
 
7
  api_key = ""
8
  selected_model = "gpt-4"
9
  summary_text = ""
 
11
  pdf_text = ""
12
 
13
  def set_api_key(user_api_key):
 
14
  global api_key, client
15
  try:
16
  api_key = user_api_key.strip()
17
  if not api_key:
18
  return "❌ API Key 不能為空"
19
+
20
  client = OpenAI(api_key=api_key)
21
+ client.chat.completions.create(
 
 
22
  model="gpt-4",
23
  messages=[{"role": "user", "content": "你好"}],
24
  max_tokens=5
25
  )
 
26
  return "✅ API Key 已設定並驗證成功"
27
  except Exception as e:
28
  return f"❌ API Key 設定失敗: {str(e)}"
29
 
30
  def set_model(model_name):
 
31
  global selected_model
32
  selected_model = model_name
33
  return f"✅ 模型已選擇:{model_name}"
34
 
35
  def extract_pdf_text(file_path):
 
36
  try:
37
  doc = fitz.open(file_path)
38
  text = ""
39
  for page_num, page in enumerate(doc):
40
  page_text = page.get_text()
41
+ if page_text.strip():
42
+ text += f"\n--- 第 {page_num + 1} 頁 ---\n{page_text}"
 
43
  doc.close()
44
  return text
45
  except Exception as e:
46
  return f"❌ PDF 解析錯誤: {str(e)}"
47
 
48
  def generate_summary(pdf_file):
 
49
  global summary_text, pdf_text
 
50
  if not client:
51
  return "❌ 請先設定 OpenAI API Key"
 
52
  if not pdf_file:
53
  return "❌ 請先上傳 PDF 文件"
 
54
  try:
 
55
  pdf_text = extract_pdf_text(pdf_file.name)
 
56
  if not pdf_text.strip():
57
  return "⚠️ 無法解析 PDF 文字,可能為純圖片 PDF 或空白文件。"
58
+ pdf_text_truncated = pdf_text[:8000]
 
 
 
 
 
 
 
 
59
  response = client.chat.completions.create(
60
  model=selected_model,
61
  messages=[
62
+ {"role": "system", "content": "請將以下 PDF 內容整理為條列式摘要,用繁體中文回答:"},
 
 
 
 
 
 
 
 
 
 
63
  {"role": "user", "content": pdf_text_truncated}
64
  ],
65
  temperature=0.3
66
  )
 
67
  summary_text = response.choices[0].message.content
68
  return summary_text
 
69
  except Exception as e:
70
+ print(traceback.format_exc())
71
+ return f" 摘要生成失敗: {str(e)}"
 
72
 
73
  def ask_question(user_question):
 
74
  if not client:
75
  return "❌ 請先設定 OpenAI API Key"
 
76
  if not summary_text and not pdf_text:
77
  return "❌ 請先生成 PDF 摘要"
 
78
  if not user_question.strip():
79
  return "❌ 請輸入問題"
 
80
  try:
 
81
  context = f"PDF 摘要:\n{summary_text}\n\n原始內容(部分):\n{pdf_text[:2000]}"
 
82
  response = client.chat.completions.create(
83
  model=selected_model,
84
  messages=[
85
+ {"role": "system", "content": f"根據以下 PDF 內容回答問題,請用繁體中文回答:\n{context}"},
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  {"role": "user", "content": user_question}
87
  ],
88
  temperature=0.2
89
  )
 
90
  return response.choices[0].message.content
 
91
  except Exception as e:
92
+ print(traceback.format_exc())
93
+ return f" 問答生成失敗: {str(e)}"
 
94
 
95
  def clear_all():
 
96
  global summary_text, pdf_text
97
  summary_text = ""
98
  pdf_text = ""
99
  return "", "", ""
100
 
 
101
  with gr.Blocks(
102
  theme=gr.themes.Soft(),
103
  title="PDF 摘要助手",
104
  css="""
 
105
  .gradio-container {
106
  max-width: none !important;
107
+ width: 100% !important;
 
108
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
109
+ min-height: 100vh;
 
110
  }
 
 
111
  .main-content {
112
  background: rgba(255, 255, 255, 0.95) !important;
113
  border-radius: 20px !important;
114
+ margin: 20px auto !important;
115
  padding: 30px !important;
116
+ max-width: 1600px !important;
117
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important;
118
  backdrop-filter: blur(10px) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  }
120
  """
121
  ) as demo:
122
  with gr.Column(elem_classes="main-content"):
123
+ gr.Markdown("# 📄 PDF 摘要 & 問答助手")
124
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  with gr.Tab("🔧 設定"):
126
+ api_key_input = gr.Textbox(label="🔑 輸入 OpenAI API Key", type="password")
127
+ api_key_status = gr.Textbox(label="狀態", interactive=False, value="等待設定 API Key...")
128
+ api_key_btn = gr.Button("確認 API Key")
129
+ api_key_btn.click(set_api_key, inputs=api_key_input, outputs=api_key_status)
130
+
131
+ model_choice = gr.Radio(["gpt-4", "gpt-4.1", "gpt-4.5"], label="選擇 AI 模型", value="gpt-4")
132
+ model_status = gr.Textbox(label="模型狀態", interactive=False, value="✅ 已選擇:gpt-4")
133
+ model_choice.change(set_model, inputs=model_choice, outputs=model_status)
134
+
135
+ with gr.Tab("📄 摘要"):
136
+ pdf_upload = gr.File(label="上傳 PDF", file_types=[".pdf"])
137
+ summary_btn = gr.Button("生成摘要")
138
+ summary_output = gr.Textbox(label="PDF 摘要", lines=12)
139
+ summary_btn.click(generate_summary, inputs=pdf_upload, outputs=summary_output)
140
+
141
+ with gr.Tab("❓ 問答"):
142
+ question_input = gr.Textbox(label="請輸入問題", lines=2)
143
+ question_btn = gr.Button("送出問題")
144
+ answer_output = gr.Textbox(label="AI 回答", lines=8)
145
+ question_btn.click(ask_question, inputs=question_input, outputs=answer_output)
146
+ question_input.submit(ask_question, inputs=question_input, outputs=answer_output)
147
+
148
+ clear_btn = gr.Button("🗑️ 清除所有資料")
149
+ clear_btn.click(clear_all, outputs=[summary_output, question_input, answer_output])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
  if __name__ == "__main__":
152
+ demo.launch(show_error=True)