Laramie2 commited on
Commit
0e73c19
·
verified ·
1 Parent(s): d7f1367

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +167 -277
app.py CHANGED
@@ -1,11 +1,11 @@
1
  import gradio as gr
2
  import os
3
- import yaml
4
  import shutil
5
  import subprocess
6
  import sys
7
  import queue
8
  import threading
 
9
  from datetime import datetime
10
  from concurrent.futures import ThreadPoolExecutor, as_completed
11
  from typing import Iterable
@@ -15,61 +15,43 @@ from gradio.themes.utils import colors, fonts, sizes
15
  # 自动安装 Playwright 浏览器(如果不存在)
16
  try:
17
  import playwright
18
- # 检查是否已经安装了浏览器,没有则下载
19
  subprocess.run(["playwright", "install", "chromium"], check=True)
20
  except Exception as e:
21
  print(f"Playwright setup failed: {e}")
22
 
23
- # Initialize environment paths
 
 
24
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
25
- PAPERS_DIR = os.path.join(BASE_DIR, "papers")
26
- CONFIG_PATH = os.path.join(BASE_DIR, "config.yaml")
27
- OUTPUT_DIR = os.path.join(BASE_DIR, "mineru_outputs")
28
- ZIP_OUTPUT_PATH = os.path.join(BASE_DIR, "mineru_results.zip")
29
-
30
- os.makedirs(PAPERS_DIR, exist_ok=True)
31
- os.makedirs(OUTPUT_DIR, exist_ok=True)
 
 
 
 
 
 
32
 
33
  # ==========================================
34
  # --- 🎨 Custom Purple Theme Definition ---
35
  # ==========================================
36
  colors.purple = colors.Color(
37
- name="purple",
38
- c50="#FAF5FF",
39
- c100="#F3E8FF",
40
- c200="#E9D5FF",
41
- c300="#DAB2FF",
42
- c400="#C084FC",
43
- c500="#A855F7",
44
- c600="#9333EA",
45
- c700="#7E22CE",
46
- c800="#6B21A8",
47
- c900="#581C87",
48
- c950="#3B0764",
49
  )
50
 
51
  class PurpleTheme(Soft):
52
- def __init__(
53
- self,
54
- *,
55
- primary_hue: colors.Color | str = colors.gray,
56
- secondary_hue: colors.Color | str = colors.purple,
57
- neutral_hue: colors.Color | str = colors.slate,
58
- text_size: sizes.Size | str = sizes.text_lg,
59
- font: fonts.Font | str | Iterable[fonts.Font | str] = (
60
- fonts.GoogleFont("Outfit"), "Arial", "sans-serif",
61
- ),
62
- font_mono: fonts.Font | str | Iterable[fonts.Font | str] = (
63
- fonts.GoogleFont("IBM Plex Mono"), "ui-monospace", "monospace",
64
- ),
65
- ):
66
  super().__init__(
67
- primary_hue=primary_hue,
68
- secondary_hue=secondary_hue,
69
- neutral_hue=neutral_hue,
70
- text_size=text_size,
71
- font=font,
72
- font_mono=font_mono,
73
  )
74
  super().set(
75
  background_fill_primary="*primary_50",
@@ -77,26 +59,14 @@ class PurpleTheme(Soft):
77
  body_background_fill="linear-gradient(135deg, *primary_200, *primary_100)",
78
  body_background_fill_dark="linear-gradient(135deg, *primary_900, *primary_800)",
79
  button_primary_text_color="white",
80
- button_primary_text_color_hover="white",
81
  button_primary_background_fill="linear-gradient(90deg, *secondary_500, *secondary_600)",
82
  button_primary_background_fill_hover="linear-gradient(90deg, *secondary_600, *secondary_700)",
83
- button_primary_background_fill_dark="linear-gradient(90deg, *secondary_600, *secondary_700)",
84
- button_primary_background_fill_hover_dark="linear-gradient(90deg, *secondary_500, *secondary_600)",
85
  button_secondary_text_color="black",
86
- button_secondary_text_color_hover="white",
87
  button_secondary_background_fill="linear-gradient(90deg, *primary_300, *primary_300)",
88
- button_secondary_background_fill_hover="linear-gradient(90deg, *primary_400, *primary_400)",
89
- button_secondary_background_fill_dark="linear-gradient(90deg, *primary_500, *primary_600)",
90
- button_secondary_background_fill_hover_dark="linear-gradient(90deg, *primary_500, *primary_500)",
91
  slider_color="*secondary_500",
92
- slider_color_dark="*secondary_600",
93
- block_title_text_weight="600",
94
  block_border_width="3px",
95
  block_shadow="*shadow_drop_lg",
96
  button_primary_shadow="*shadow_drop_lg",
97
- button_large_padding="11px",
98
- color_accent_soft="*primary_100",
99
- block_label_background_fill="*primary_200",
100
  )
101
 
102
  purple_theme = PurpleTheme()
@@ -106,19 +76,15 @@ purple_theme = PurpleTheme()
106
  # ==========================================
107
 
108
  def get_tree_html(dir_path):
109
- """递归生成可折叠的 HTML 目录树"""
110
  if not os.path.exists(dir_path):
111
  return "<div style='margin-left: 15px; color: #888;'><i>Directory missing</i></div>"
112
 
113
  def build_html(current_path):
114
  html = ""
115
- try:
116
- items = sorted(os.listdir(current_path))
117
- except Exception:
118
- return ""
119
 
120
- if not items:
121
- return ""
122
 
123
  for item in items:
124
  item_path = os.path.join(current_path, item)
@@ -134,13 +100,14 @@ def get_tree_html(dir_path):
134
  content = build_html(dir_path)
135
  return content if content else "<div style='margin-left: 15px; color: #888;'><i>Empty directory</i></div>"
136
 
137
- def get_debug_info():
138
- """返回渲染好的目录树 HTML 容器"""
139
- papers_tree = get_tree_html(PAPERS_DIR)
140
- output_tree = get_tree_html(OUTPUT_DIR)
141
 
142
  html = f"""
143
  <div style="font-family: 'IBM Plex Mono', monospace; font-size: 13px; background-color: #1e1e1e; border: 1px solid #C084FC; border-radius: 8px; padding: 16px; max-height: 400px; overflow-y: auto;">
 
144
  <details open style="margin-bottom: 12px; cursor: pointer;">
145
  <summary style="outline: none; font-size: 15px; color: #A855F7;">📁 <b>papers/</b></summary>
146
  {papers_tree}
@@ -153,153 +120,128 @@ def get_debug_info():
153
  """
154
  return html
155
 
156
- def save_api_settings(api_key, api_base_url=None):
 
157
  if not api_key:
158
- return "❌ Key cannot be empty", get_debug_info(), False
159
- try:
160
- config = {}
161
- if os.path.exists(CONFIG_PATH):
162
- with open(CONFIG_PATH, "r", encoding="utf-8") as f:
163
- config = yaml.safe_load(f) or {}
164
- config.setdefault("api_keys", {})["gemini_api_key"] = api_key
165
- if api_base_url:
166
- config["api_base_url"] = api_base_url
167
- with open(CONFIG_PATH, "w", encoding="utf-8") as f:
168
- yaml.dump(config, f, allow_unicode=True)
169
-
170
- success_msg = "✅ Key saved"
171
- if api_base_url:
172
- success_msg += ", Base URL updated"
173
-
174
- return success_msg, get_debug_info(), True
175
- except Exception as e:
176
- return f"❌ Error: {str(e)}", get_debug_info(), False
177
 
178
- def save_pdf(file):
179
- if file is None: return "❌ Please select a PDF first", get_debug_info(), False
180
  try:
181
- for f in os.listdir(PAPERS_DIR):
182
- file_to_del = os.path.join(PAPERS_DIR, f)
183
- if os.path.isfile(file_to_del):
184
- os.remove(file_to_del)
 
 
185
 
186
- file_path = os.path.join(PAPERS_DIR, os.path.basename(file.name))
187
  shutil.copy(file.name, file_path)
188
- return f"✅ Saved: {os.path.basename(file.name)}", get_debug_info(), True
189
  except Exception as e:
190
- return f"❌ Error: {str(e)}", get_debug_info(), False
191
 
192
- def clear_pdf():
193
- """清空 PDF 并锁定后续所有步骤"""
194
  try:
195
- deleted_files = []
196
- deleted_dirs = []
197
-
198
- pdf_names = [f for f in os.listdir(PAPERS_DIR) if f.endswith('.pdf')]
199
-
200
- for f in os.listdir(PAPERS_DIR):
201
- file_to_del = os.path.join(PAPERS_DIR, f)
202
- if os.path.isfile(file_to_del):
203
- os.remove(file_to_del)
204
- deleted_files.append(f)
205
-
206
- if os.path.exists(OUTPUT_DIR):
207
- for pdf_name in pdf_names:
208
- base_name = os.path.splitext(pdf_name)[0]
209
- base_name_us = base_name.replace(" ", "_")
210
-
211
- for item in os.listdir(OUTPUT_DIR):
212
- if item in [base_name, base_name_us, pdf_name]:
213
- dir_to_del = os.path.join(OUTPUT_DIR, item)
214
- if os.path.isdir(dir_to_del):
215
- shutil.rmtree(dir_to_del)
216
- deleted_dirs.append(item)
217
-
218
- if os.path.exists(ZIP_OUTPUT_PATH):
219
- os.remove(ZIP_OUTPUT_PATH)
220
 
221
  disable_btn = gr.update(interactive=False)
222
- if deleted_files or deleted_dirs:
223
- return f"🗑️ Workspace cleared", get_debug_info(), False, disable_btn, disable_btn, disable_btn, disable_btn
224
- return "ℹ️ Workspace is already empty", get_debug_info(), False, disable_btn, disable_btn, disable_btn, disable_btn
225
  except Exception as e:
226
- return f"❌ Error deleting file: {str(e)}", get_debug_info(), False, gr.update(), gr.update(), gr.update(), gr.update()
227
-
228
- def run_mineru_parsing_and_dag_gen():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  no_change = gr.update()
230
  disable_btn = gr.update(interactive=False)
 
231
 
232
- # 进度条显示 (visible=True)
233
- show_progress = gr.update(visible=True)
234
- hide_progress = gr.update(visible=False)
235
-
236
- if not os.path.exists(PAPERS_DIR) or not any(f.endswith('.pdf') for f in os.listdir(PAPERS_DIR)):
237
- yield "❌ No PDF file found", get_debug_info(), "No execution logs.", no_change, no_change, no_change, no_change, hide_progress
238
  return
239
 
240
  full_log = ""
241
  try:
242
- env = os.environ.copy()
243
- env["MINERU_FORMULA_ENABLE"] = "false"
244
- env["MINERU_TABLE_ENABLE"] = "false"
245
- env["MINERU_DEVICE_MODE"] = "cpu"
246
- env["MINERU_VIRTUAL_VRAM_SIZE"] = "8"
247
-
248
- command_mineru = ["mineru", "-p", PAPERS_DIR, "-o", OUTPUT_DIR]
249
- full_log += "--- Mineru Executing ---\n"
250
 
251
- # 激活显示全局进度条
252
- yield "⏳ Executing Mineru parsing...", get_debug_info(), full_log, no_change, no_change, no_change, no_change, show_progress
253
 
254
  process_mineru = subprocess.Popen(
255
  command_mineru, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1
256
  )
 
 
257
  for line in iter(process_mineru.stdout.readline, ''):
258
  full_log += line
259
- yield "⏳ Executing Mineru parsing...", get_debug_info(), full_log, no_change, no_change, no_change, no_change, show_progress
260
  process_mineru.stdout.close()
261
  returncode_mineru = process_mineru.wait()
262
 
263
  if returncode_mineru != 0:
264
- yield f"Mineru parsing failed (Exit Code: {returncode_mineru})", get_debug_info(), full_log, disable_btn, disable_btn, disable_btn, disable_btn, hide_progress
 
265
  return
266
 
267
  command_dag = [sys.executable, "gen_dag.py"]
268
  full_log += "\n--- DAG Gen Executing ---\n"
269
- yield " Mineru parsing complete, executing DAG generation...", get_debug_info(), full_log, no_change, no_change, no_change, no_change, show_progress
 
270
 
271
  process_dag = subprocess.Popen(
272
- command_dag, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1
273
  )
 
 
274
  for line in iter(process_dag.stdout.readline, ''):
275
  full_log += line
276
- yield "⏳ Executing DAG generation...", get_debug_info(), full_log, no_change, no_change, no_change, no_change, show_progress
277
  process_dag.stdout.close()
278
  returncode_dag = process_dag.wait()
279
 
280
- # 解析完全成功,解锁第三部分的按钮,并隐藏进度条
281
  if returncode_dag == 0:
282
- status = "✅ PDF parsing & DAG generation fully completed"
283
  enable_btn = gr.update(interactive=True)
284
- yield status, get_debug_info(), full_log, enable_btn, enable_btn, enable_btn, enable_btn, hide_progress
285
  else:
286
- status = f"DAG generation failed (Exit Code: {returncode_dag})"
287
- yield status, get_debug_info(), full_log, disable_btn, disable_btn, disable_btn, disable_btn, hide_progress
288
 
289
  except Exception as e:
290
- error_log = full_log + f"\n[Global Exception] Exception occurred:\n{str(e)}"
291
- yield "❌ Execution Exception", get_debug_info(), error_log, disable_btn, disable_btn, disable_btn, disable_btn, hide_progress
 
292
 
293
 
294
- def run_final_generation(task_type="all"):
295
- """
296
- 使用队列和多线程实时获取子进程的流式输出,并通过 yield 推送到前端界面。
297
- """
298
- show_progress = gr.update(visible=True)
299
- hide_progress = gr.update(visible=False)
300
-
301
- if not os.path.exists(OUTPUT_DIR):
302
- yield "❌ Please run the parsing step first", get_debug_info(), "No output folder found.", gr.update(visible=False), hide_progress
303
  return
304
 
305
  scripts_to_run = []
@@ -307,16 +249,14 @@ def run_final_generation(task_type="all"):
307
  elif task_type == "poster": scripts_to_run = ["gen_poster.py"]
308
  elif task_type == "pr": scripts_to_run = ["gen_pr.py"]
309
  elif task_type == "all": scripts_to_run = ["gen_ppt.py", "gen_poster.py", "gen_pr.py"]
310
- else:
311
- yield "❌ Unknown task type", get_debug_info(), "Invalid task_type.", gr.update(visible=False), hide_progress
312
- return
313
-
314
- full_log = f"🚀 Preparing to start {len(scripts_to_run)} tasks...\n"
315
- # 激活全局进度条
316
- yield f"⏳ Starting {task_type.upper()} generation...", get_debug_info(), full_log, gr.update(visible=False), show_progress
317
 
318
  q = queue.Queue()
319
  processes = []
 
320
 
321
  def enqueue_output(out, script_name):
322
  for line in iter(out.readline, ''):
@@ -326,11 +266,7 @@ def run_final_generation(task_type="all"):
326
  try:
327
  for script in scripts_to_run:
328
  p = subprocess.Popen(
329
- [sys.executable, script],
330
- stdout=subprocess.PIPE,
331
- stderr=subprocess.STDOUT,
332
- text=True,
333
- bufsize=1
334
  )
335
  processes.append((script, p))
336
  t = threading.Thread(target=enqueue_output, args=(p.stdout, script))
@@ -338,36 +274,38 @@ def run_final_generation(task_type="all"):
338
  t.start()
339
 
340
  active_processes = len(processes)
 
 
341
  while active_processes > 0 or not q.empty():
342
  try:
343
  script_name, line = q.get(timeout=0.1)
344
  full_log += f"[{script_name}] {line}"
345
- yield f"⏳ Generating {task_type.upper()}...", get_debug_info(), full_log, gr.update(visible=False), show_progress
346
  except queue.Empty:
347
  active_processes = sum(1 for _, p in processes if p.poll() is None)
348
 
349
- success = True
350
- for script, p in processes:
351
- if p.returncode != 0:
352
- success = False
353
- full_log += f"\n❌ [Error] {script} returned non-zero exit code (Exit Code: {p.returncode})\n"
354
 
355
  if not success:
356
- yield f"❌ {task_type.upper()} contains failed tasks, please check logs", get_debug_info(), full_log, gr.update(visible=False), hide_progress
 
357
  return
358
 
359
  full_log += "\n📦 Zipping output directory...\n"
360
- yield f"⏳ Zipping outputs...", get_debug_info(), full_log, gr.update(visible=False), show_progress
 
361
 
362
- zip_base_name = ZIP_OUTPUT_PATH.replace(".zip", "")
363
- shutil.make_archive(zip_base_name, 'zip', OUTPUT_DIR)
364
 
365
- full_log += "✅ All tasks and zipping completed successfully.\n"
366
- yield f"✅ {task_type.upper()} generated and zipped successfully", get_debug_info(), full_log, gr.update(value=ZIP_OUTPUT_PATH, visible=True), hide_progress
 
367
 
368
  except Exception as e:
369
- error_log = full_log + f"\n[Global Exception] Exception occurred:\n{str(e)}"
370
- yield "❌ Global exception during final generation", get_debug_info(), error_log, gr.update(visible=False), hide_progress
 
371
 
372
 
373
  # ==========================================
@@ -401,29 +339,10 @@ body, .gradio-container {
401
 
402
  @keyframes gradient-shift { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
403
 
404
- #subtitle { text-align: center !important; margin-bottom: 1.5rem; }
405
  #subtitle p { margin: 0 auto; color: #666; font-size: 1.1rem; font-weight: 500; }
406
  .dark #subtitle p { color: #DAB2FF; }
407
 
408
- /* ======== CSS FOR GLOBAL PROGRESS BAR ======== */
409
- #global-progress { margin-bottom: 20px !important; transition: opacity 0.3s ease; }
410
- .progress-container {
411
- width: 100%; background-color: rgba(243, 232, 255, 0.8);
412
- border: 1px solid #C084FC; border-radius: 16px; overflow: hidden;
413
- position: relative; height: 38px; display: flex; align-items: center; justify-content: center;
414
- box-shadow: 0 4px 15px rgba(168, 85, 247, 0.15);
415
- }
416
- .progress-bar {
417
- height: 100%; width: 40%;
418
- background: linear-gradient(90deg, transparent, rgba(168, 85, 247, 0.6), rgba(192, 132, 252, 0.8), rgba(168, 85, 247, 0.6), transparent);
419
- position: absolute; left: -40%; top: 0;
420
- animation: loading-slide 1.5s infinite linear;
421
- }
422
- @keyframes loading-slide { 0% { left: -40%; } 100% { left: 100%; } }
423
- .progress-text { position: relative; z-index: 1; color: #6B21A8; font-weight: 600; font-size: 15px; letter-spacing: 0.5px; }
424
- .dark .progress-container { background-color: rgba(30, 30, 30, 0.9); border-color: rgba(168, 85, 247, 0.4); }
425
- .dark .progress-text { color: #DAB2FF; }
426
-
427
  .gradio-group {
428
  background: rgba(255, 255, 255, 0.9) !important;
429
  border: 2px solid #E9D5FF !important;
@@ -498,7 +417,7 @@ body, .gradio-container {
498
  margin-right: auto !important;
499
  margin-top: 10px !important;
500
  margin-bottom: 10px !important;
501
- display: block !important;
502
  transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.3s ease !important;
503
  box-shadow: 0 4px 15px rgba(126, 34, 206, 0.3) !important;
504
  cursor: pointer !important;
@@ -566,7 +485,11 @@ details > summary:hover { color: #E9D5FF !important; }
566
  """
567
 
568
  with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
569
- # 定义全局状态变量,用于实现依赖解锁
 
 
 
 
570
  api_saved_state = gr.State(False)
571
  pdf_ready_state = gr.State(False)
572
 
@@ -574,17 +497,8 @@ with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
574
  gr.Markdown("# **PaperX Platform**", elem_id="main-title")
575
  gr.Markdown("One-click parsing of academic PDFs, DAG structuring, and multi-modal asset generation.", elem_id="subtitle")
576
 
577
- # 🟢 新增的全局加载动画条,默认隐藏
578
- global_progress = gr.HTML(
579
- '<div class="progress-container"><div class="progress-bar"></div><div class="progress-text">⏳ 加载中... (Task in progress, please wait)</div></div>',
580
- visible=False,
581
- elem_id="global-progress"
582
- )
583
-
584
  with gr.Row():
585
- # ================= LEFT COLUMN: SETTINGS & ACTIONS =================
586
  with gr.Column(scale=1):
587
-
588
  # 1. API Configuration
589
  with gr.Group(elem_classes="gradio-group"):
590
  gr.Markdown("### ⚙️ 1. Global API Configuration")
@@ -592,112 +506,88 @@ with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
592
  key_input = gr.Textbox(label="API Key", type="password", placeholder="sk-...", scale=1)
593
  api_base_url_input = gr.Textbox(label="Base URL (Optional)", placeholder="https://api.example.com", scale=1)
594
  key_btn = gr.Button("💾 Save API Configuration")
595
-
596
- api_status = gr.Textbox(
597
- show_label=False, placeholder="Waiting for API configuration...", lines=1, interactive=False, elem_classes="status-text"
598
- )
599
-
600
  # 2. Document Parsing
601
  with gr.Group(elem_classes="gradio-group"):
602
  gr.Markdown("### 📄 2. Document Parsing")
603
-
604
- pdf_input = gr.File(
605
- label="Upload Document",
606
- file_types=[".pdf"],
607
- elem_id="pdf-upload-box"
608
- )
609
-
610
- # 默认锁定
611
  parse_btn = gr.Button("🚀 Start Mineru & DAG Extraction", elem_classes="primary-action-btn", interactive=False)
612
-
613
- parse_status = gr.Textbox(
614
- show_label=False, placeholder="Waiting for document upload...", lines=1, interactive=False, elem_classes="status-text"
615
- )
616
 
617
  # 3. Asset Generation
618
  with gr.Group(elem_classes="gradio-group"):
619
  gr.Markdown("### 🎯 3. Asset Generation")
620
- gr.Markdown("Generate final formats based on DAG structure:")
621
-
622
  with gr.Row(elem_classes="action-row"):
623
- # 默认锁定
624
  gen_ppt_btn = gr.Button("📊 Gen PPT", elem_classes="action-btn", interactive=False)
625
  gen_poster_btn = gr.Button("🖼️ Gen Poster", elem_classes="action-btn", interactive=False)
626
  gen_pr_btn = gr.Button("📰 Gen PR", elem_classes="action-btn", interactive=False)
627
-
628
- # 默认锁定
629
  gen_all_btn = gr.Button("✨ Generate All Assets (ALL)", elem_classes="primary-action-btn", interactive=False)
630
 
631
- # ================= RIGHT COLUMN: OUTPUTS & LOGS =================
632
  with gr.Column(scale=1):
633
-
634
  # 4. Results & Downloads
635
  with gr.Group(elem_classes="gradio-group"):
636
  gr.Markdown("### 📦 Generation Results & Download")
637
- gen_status = gr.Textbox(
638
- show_label=False, placeholder="No generation task currently...", lines=2, interactive=False, elem_classes="status-text"
639
- )
640
  download_file = gr.File(label="📥 Get Final Zip Archive", interactive=False, visible=False)
641
 
642
- # 5. Debugging & Terminal
643
  with gr.Group(elem_classes="gradio-group"):
644
  gr.Markdown("### 🛠️ Developer Monitoring (Debug Only)")
645
  with gr.Tabs():
646
  with gr.Tab("📜 Terminal Stream"):
647
- cmd_logs = gr.Textbox(
648
- label="Stdout / Stderr", placeholder="Waiting for task to start...", lines=14, interactive=False, elem_classes="log-box"
649
- )
650
-
651
  with gr.Tab("🔍 System Snapshot"):
652
  refresh_btn = gr.Button("🔄 Refresh Directory Tree")
653
- debug_view = gr.HTML(
654
- value=get_debug_info()
655
- )
 
656
 
657
  # ================= LOGIC BINDINGS =================
658
 
659
- # [步骤 1] 配置 API
660
  key_btn.click(
661
  fn=save_api_settings,
662
- inputs=[key_input, api_base_url_input],
663
- outputs=[api_status, debug_view, api_saved_state]
664
  )
665
 
666
- # [步骤 2.1] 上传 PDF
667
- pdf_input.upload(fn=save_pdf, inputs=pdf_input, outputs=[parse_status, debug_view, pdf_ready_state])
 
 
 
668
 
669
- # [步骤 2.2] 清除 PDF
670
  pdf_input.clear(
671
  fn=clear_pdf,
 
672
  outputs=[parse_status, debug_view, pdf_ready_state, gen_ppt_btn, gen_poster_btn, gen_pr_btn, gen_all_btn]
673
  )
674
 
675
- # [依赖检查] 当 api 状态或 pdf 状态发生变化时,检查是否解锁"开始解析"按钮
676
  def check_parse_btn_ready(api_ready, pdf_ready):
677
  return gr.update(interactive=(api_ready and pdf_ready))
678
 
679
  api_saved_state.change(fn=check_parse_btn_ready, inputs=[api_saved_state, pdf_ready_state], outputs=parse_btn)
680
  pdf_ready_state.change(fn=check_parse_btn_ready, inputs=[api_saved_state, pdf_ready_state], outputs=parse_btn)
681
 
682
- # [步骤 2.3] 执行解析,绑定 8 个 outputs (新增控制 global_progress)
683
  parse_btn.click(
684
  fn=run_mineru_parsing_and_dag_gen,
685
- outputs=[parse_status, debug_view, cmd_logs, gen_ppt_btn, gen_poster_btn, gen_pr_btn, gen_all_btn, global_progress]
 
686
  )
687
 
688
- # [步骤 3] 资产生成
689
- def trigger_gen_ppt(): yield from run_final_generation("ppt")
690
- def trigger_gen_poster(): yield from run_final_generation("poster")
691
- def trigger_gen_pr(): yield from run_final_generation("pr")
692
- def trigger_gen_all(): yield from run_final_generation("all")
693
-
694
- # [步骤 3 绑定] 绑定 5 个 outputs (新增控制 global_progress)
695
- gen_ppt_btn.click(fn=trigger_gen_ppt, outputs=[gen_status, debug_view, cmd_logs, download_file, global_progress])
696
- gen_poster_btn.click(fn=trigger_gen_poster, outputs=[gen_status, debug_view, cmd_logs, download_file, global_progress])
697
- gen_pr_btn.click(fn=trigger_gen_pr, outputs=[gen_status, debug_view, cmd_logs, download_file, global_progress])
698
- gen_all_btn.click(fn=trigger_gen_all, outputs=[gen_status, debug_view, cmd_logs, download_file, global_progress])
699
 
700
- refresh_btn.click(fn=get_debug_info, outputs=debug_view)
701
 
702
  if __name__ == "__main__":
703
  demo.launch()
 
1
  import gradio as gr
2
  import os
 
3
  import shutil
4
  import subprocess
5
  import sys
6
  import queue
7
  import threading
8
+ import uuid
9
  from datetime import datetime
10
  from concurrent.futures import ThreadPoolExecutor, as_completed
11
  from typing import Iterable
 
15
  # 自动安装 Playwright 浏览器(如果不存在)
16
  try:
17
  import playwright
 
18
  subprocess.run(["playwright", "install", "chromium"], check=True)
19
  except Exception as e:
20
  print(f"Playwright setup failed: {e}")
21
 
22
+ # ==========================================
23
+ # --- 📁 全局目录配置 (修改为 Session 基础目录) ---
24
+ # ==========================================
25
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
26
+ SESSIONS_BASE_DIR = os.path.join(BASE_DIR, "user_sessions")
27
+ os.makedirs(SESSIONS_BASE_DIR, exist_ok=True)
28
+
29
+ def get_user_dirs(session_id):
30
+ """根据 Session ID 生成用户专属的隔离目录"""
31
+ user_base = os.path.join(SESSIONS_BASE_DIR, session_id)
32
+ papers_dir = os.path.join(user_base, "papers")
33
+ output_dir = os.path.join(user_base, "mineru_outputs")
34
+ zip_path = os.path.join(user_base, "mineru_results.zip")
35
+
36
+ os.makedirs(papers_dir, exist_ok=True)
37
+ os.makedirs(output_dir, exist_ok=True)
38
+ return papers_dir, output_dir, zip_path
39
 
40
  # ==========================================
41
  # --- 🎨 Custom Purple Theme Definition ---
42
  # ==========================================
43
  colors.purple = colors.Color(
44
+ name="purple", c50="#FAF5FF", c100="#F3E8FF", c200="#E9D5FF",
45
+ c300="#DAB2FF", c400="#C084FC", c500="#A855F7", c600="#9333EA",
46
+ c700="#7E22CE", c800="#6B21A8", c900="#581C87", c950="#3B0764",
 
 
 
 
 
 
 
 
 
47
  )
48
 
49
  class PurpleTheme(Soft):
50
+ def __init__(self, **kwargs):
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  super().__init__(
52
+ primary_hue=colors.gray, secondary_hue=colors.purple, neutral_hue=colors.slate,
53
+ font=(fonts.GoogleFont("Outfit"), "Arial", "sans-serif"),
54
+ font_mono=(fonts.GoogleFont("IBM Plex Mono"), "ui-monospace", "monospace"),
 
 
 
55
  )
56
  super().set(
57
  background_fill_primary="*primary_50",
 
59
  body_background_fill="linear-gradient(135deg, *primary_200, *primary_100)",
60
  body_background_fill_dark="linear-gradient(135deg, *primary_900, *primary_800)",
61
  button_primary_text_color="white",
 
62
  button_primary_background_fill="linear-gradient(90deg, *secondary_500, *secondary_600)",
63
  button_primary_background_fill_hover="linear-gradient(90deg, *secondary_600, *secondary_700)",
 
 
64
  button_secondary_text_color="black",
 
65
  button_secondary_background_fill="linear-gradient(90deg, *primary_300, *primary_300)",
 
 
 
66
  slider_color="*secondary_500",
 
 
67
  block_border_width="3px",
68
  block_shadow="*shadow_drop_lg",
69
  button_primary_shadow="*shadow_drop_lg",
 
 
 
70
  )
71
 
72
  purple_theme = PurpleTheme()
 
76
  # ==========================================
77
 
78
  def get_tree_html(dir_path):
 
79
  if not os.path.exists(dir_path):
80
  return "<div style='margin-left: 15px; color: #888;'><i>Directory missing</i></div>"
81
 
82
  def build_html(current_path):
83
  html = ""
84
+ try: items = sorted(os.listdir(current_path))
85
+ except Exception: return ""
 
 
86
 
87
+ if not items: return ""
 
88
 
89
  for item in items:
90
  item_path = os.path.join(current_path, item)
 
100
  content = build_html(dir_path)
101
  return content if content else "<div style='margin-left: 15px; color: #888;'><i>Empty directory</i></div>"
102
 
103
+ def get_debug_info(session_id):
104
+ papers_dir, output_dir, _ = get_user_dirs(session_id)
105
+ papers_tree = get_tree_html(papers_dir)
106
+ output_tree = get_tree_html(output_dir)
107
 
108
  html = f"""
109
  <div style="font-family: 'IBM Plex Mono', monospace; font-size: 13px; background-color: #1e1e1e; border: 1px solid #C084FC; border-radius: 8px; padding: 16px; max-height: 400px; overflow-y: auto;">
110
+ <div style="color: #888; margin-bottom: 8px;">Session ID: {session_id[:8]}...</div>
111
  <details open style="margin-bottom: 12px; cursor: pointer;">
112
  <summary style="outline: none; font-size: 15px; color: #A855F7;">📁 <b>papers/</b></summary>
113
  {papers_tree}
 
120
  """
121
  return html
122
 
123
+ def save_api_settings(api_key, api_base_url, session_id):
124
+ """不再写入全局 yaml,而是保存在 Gradio State 并在运行时通过环境变量注入"""
125
  if not api_key:
126
+ return "❌ Key cannot be empty", get_debug_info(session_id), False, "", ""
127
+
128
+ success_msg = "✅ Key saved securely in session memory"
129
+ if api_base_url: success_msg += ", Base URL updated"
130
+
131
+ return success_msg, get_debug_info(session_id), True, api_key, api_base_url
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
+ def save_pdf(file, session_id):
134
+ if file is None: return "❌ Please select a PDF first", get_debug_info(session_id), False
135
  try:
136
+ papers_dir, _, _ = get_user_dirs(session_id)
137
+
138
+ # 清理旧文件
139
+ for f in os.listdir(papers_dir):
140
+ file_to_del = os.path.join(papers_dir, f)
141
+ if os.path.isfile(file_to_del): os.remove(file_to_del)
142
 
143
+ file_path = os.path.join(papers_dir, os.path.basename(file.name))
144
  shutil.copy(file.name, file_path)
145
+ return f"✅ Saved: {os.path.basename(file.name)}", get_debug_info(session_id), True
146
  except Exception as e:
147
+ return f"❌ Error: {str(e)}", get_debug_info(session_id), False
148
 
149
+ def clear_pdf(session_id):
 
150
  try:
151
+ user_base = os.path.join(SESSIONS_BASE_DIR, session_id)
152
+ if os.path.exists(user_base):
153
+ shutil.rmtree(user_base) # 直接删除该用户的整个工作区
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
  disable_btn = gr.update(interactive=False)
156
+ return f"🗑️ Workspace cleared", get_debug_info(session_id), False, disable_btn, disable_btn, disable_btn, disable_btn
 
 
157
  except Exception as e:
158
+ return f"❌ Error deleting: {str(e)}", get_debug_info(session_id), False, gr.update(), gr.update(), gr.update(), gr.update()
159
+
160
+ def build_user_env(api_key, api_base_url, papers_dir, output_dir):
161
+ """构建用户专属的子进程环境变量"""
162
+ env = os.environ.copy()
163
+ env["MINERU_FORMULA_ENABLE"] = "false"
164
+ env["MINERU_TABLE_ENABLE"] = "false"
165
+ env["MINERU_DEVICE_MODE"] = "cpu"
166
+ env["MINERU_VIRTUAL_VRAM_SIZE"] = "8"
167
+
168
+ # 注入专属 API 配置和路径
169
+ if api_key: env["GEMINI_API_KEY"] = api_key
170
+ if api_base_url: env["GEMINI_API_BASE_URL"] = api_base_url
171
+ env["USER_PAPERS_DIR"] = papers_dir
172
+ env["USER_OUTPUT_DIR"] = output_dir
173
+ return env
174
+
175
+ def run_mineru_parsing_and_dag_gen(session_id, api_key, api_base_url, progress=gr.Progress()):
176
  no_change = gr.update()
177
  disable_btn = gr.update(interactive=False)
178
+ papers_dir, output_dir, _ = get_user_dirs(session_id)
179
 
180
+ if not os.path.exists(papers_dir) or not any(f.endswith('.pdf') for f in os.listdir(papers_dir)):
181
+ yield "❌ No PDF file found", get_debug_info(session_id), "No execution logs.", no_change, no_change, no_change, no_change
 
 
 
 
182
  return
183
 
184
  full_log = ""
185
  try:
186
+ env = build_user_env(api_key, api_base_url, papers_dir, output_dir)
187
+
188
+ command_mineru = ["mineru", "-p", papers_dir, "-o", output_dir]
189
+ full_log += f"--- Mineru Executing (Session: {session_id[:8]}) ---\n"
 
 
 
 
190
 
191
+ progress(0.1, desc="启动 Mineru 解析...")
192
+ yield "⏳ Executing Mineru parsing...", get_debug_info(session_id), full_log, no_change, no_change, no_change, no_change
193
 
194
  process_mineru = subprocess.Popen(
195
  command_mineru, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1
196
  )
197
+
198
+ progress(0.3, desc="Mineru 正在解析 PDF...")
199
  for line in iter(process_mineru.stdout.readline, ''):
200
  full_log += line
201
+ yield "⏳ Executing Mineru parsing...", get_debug_info(session_id), full_log, no_change, no_change, no_change, no_change
202
  process_mineru.stdout.close()
203
  returncode_mineru = process_mineru.wait()
204
 
205
  if returncode_mineru != 0:
206
+ progress(1.0, desc="Mineru 解析失败")
207
+ yield f"❌ Mineru parsing failed (Exit Code: {returncode_mineru})", get_debug_info(session_id), full_log, disable_btn, disable_btn, disable_btn, disable_btn
208
  return
209
 
210
  command_dag = [sys.executable, "gen_dag.py"]
211
  full_log += "\n--- DAG Gen Executing ---\n"
212
+ progress(0.6, desc="执行 DAG 生成...")
213
+ yield "⏳ Executing DAG generation...", get_debug_info(session_id), full_log, no_change, no_change, no_change, no_change
214
 
215
  process_dag = subprocess.Popen(
216
+ command_dag, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1
217
  )
218
+
219
+ progress(0.8, desc="构建图结构中...")
220
  for line in iter(process_dag.stdout.readline, ''):
221
  full_log += line
222
+ yield "⏳ Executing DAG generation...", get_debug_info(session_id), full_log, no_change, no_change, no_change, no_change
223
  process_dag.stdout.close()
224
  returncode_dag = process_dag.wait()
225
 
 
226
  if returncode_dag == 0:
227
+ progress(1.0, desc="解析与构建完成!")
228
  enable_btn = gr.update(interactive=True)
229
+ yield "✅ PDF parsing & DAG generation fully completed", get_debug_info(session_id), full_log, enable_btn, enable_btn, enable_btn, enable_btn
230
  else:
231
+ progress(1.0, desc="DAG 生成失败")
232
+ yield f"❌ DAG generation failed", get_debug_info(session_id), full_log, disable_btn, disable_btn, disable_btn, disable_btn
233
 
234
  except Exception as e:
235
+ progress(1.0, desc="发生异常")
236
+ error_log = full_log + f"\n[Global Exception]:\n{str(e)}"
237
+ yield "❌ Execution Exception", get_debug_info(session_id), error_log, disable_btn, disable_btn, disable_btn, disable_btn
238
 
239
 
240
+ def run_final_generation(task_type, session_id, api_key, api_base_url, progress=gr.Progress()):
241
+ papers_dir, output_dir, zip_path = get_user_dirs(session_id)
242
+
243
+ if not os.path.exists(output_dir):
244
+ yield "❌ Please run parsing first", get_debug_info(session_id), "No output folder found.", gr.update(visible=False)
 
 
 
 
245
  return
246
 
247
  scripts_to_run = []
 
249
  elif task_type == "poster": scripts_to_run = ["gen_poster.py"]
250
  elif task_type == "pr": scripts_to_run = ["gen_pr.py"]
251
  elif task_type == "all": scripts_to_run = ["gen_ppt.py", "gen_poster.py", "gen_pr.py"]
252
+
253
+ full_log = f"🚀 Starting {len(scripts_to_run)} tasks for session {session_id[:8]}...\n"
254
+ progress(0.1, desc=f"启动 {task_type.upper()} 生成任务...")
255
+ yield f"⏳ Starting {task_type.upper()} generation...", get_debug_info(session_id), full_log, gr.update(visible=False)
 
 
 
256
 
257
  q = queue.Queue()
258
  processes = []
259
+ env = build_user_env(api_key, api_base_url, papers_dir, output_dir)
260
 
261
  def enqueue_output(out, script_name):
262
  for line in iter(out.readline, ''):
 
266
  try:
267
  for script in scripts_to_run:
268
  p = subprocess.Popen(
269
+ [sys.executable, script], env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1
 
 
 
 
270
  )
271
  processes.append((script, p))
272
  t = threading.Thread(target=enqueue_output, args=(p.stdout, script))
 
274
  t.start()
275
 
276
  active_processes = len(processes)
277
+ progress(0.5, desc=f"正在并行生成 {task_type.upper()}...")
278
+
279
  while active_processes > 0 or not q.empty():
280
  try:
281
  script_name, line = q.get(timeout=0.1)
282
  full_log += f"[{script_name}] {line}"
283
+ yield f"⏳ Generating {task_type.upper()}...", get_debug_info(session_id), full_log, gr.update(visible=False)
284
  except queue.Empty:
285
  active_processes = sum(1 for _, p in processes if p.poll() is None)
286
 
287
+ success = all(p.returncode == 0 for _, p in processes)
 
 
 
 
288
 
289
  if not success:
290
+ progress(1.0, desc="生成失败")
291
+ yield f"❌ Tasks failed, check logs", get_debug_info(session_id), full_log, gr.update(visible=False)
292
  return
293
 
294
  full_log += "\n📦 Zipping output directory...\n"
295
+ progress(0.9, desc="打包压缩结果...")
296
+ yield f"⏳ Zipping outputs...", get_debug_info(session_id), full_log, gr.update(visible=False)
297
 
298
+ zip_base_name = zip_path.replace(".zip", "")
299
+ shutil.make_archive(zip_base_name, 'zip', output_dir)
300
 
301
+ full_log += "✅ All tasks completed successfully.\n"
302
+ progress(1.0, desc="全部完成!")
303
+ yield f"✅ {task_type.upper()} successfully generated", get_debug_info(session_id), full_log, gr.update(value=zip_path, visible=True)
304
 
305
  except Exception as e:
306
+ progress(1.0, desc="发生全局异常")
307
+ error_log = full_log + f"\n[Global Exception]:\n{str(e)}"
308
+ yield "❌ Global exception", get_debug_info(session_id), error_log, gr.update(visible=False)
309
 
310
 
311
  # ==========================================
 
339
 
340
  @keyframes gradient-shift { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
341
 
342
+ #subtitle { text-align: center !important; margin-bottom: 2rem; }
343
  #subtitle p { margin: 0 auto; color: #666; font-size: 1.1rem; font-weight: 500; }
344
  .dark #subtitle p { color: #DAB2FF; }
345
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  .gradio-group {
347
  background: rgba(255, 255, 255, 0.9) !important;
348
  border: 2px solid #E9D5FF !important;
 
417
  margin-right: auto !important;
418
  margin-top: 10px !important;
419
  margin-bottom: 10px !important;
420
+ display: block !important;
421
  transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.3s ease !important;
422
  box-shadow: 0 4px 15px rgba(126, 34, 206, 0.3) !important;
423
  cursor: pointer !important;
 
485
  """
486
 
487
  with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
488
+ # 核心:为每个连接的用户生成唯一的 Session ID 和存放凭证的状态
489
+ session_id_state = gr.State(lambda: str(uuid.uuid4()))
490
+ user_api_key_state = gr.State("")
491
+ user_api_base_state = gr.State("")
492
+
493
  api_saved_state = gr.State(False)
494
  pdf_ready_state = gr.State(False)
495
 
 
497
  gr.Markdown("# **PaperX Platform**", elem_id="main-title")
498
  gr.Markdown("One-click parsing of academic PDFs, DAG structuring, and multi-modal asset generation.", elem_id="subtitle")
499
 
 
 
 
 
 
 
 
500
  with gr.Row():
 
501
  with gr.Column(scale=1):
 
502
  # 1. API Configuration
503
  with gr.Group(elem_classes="gradio-group"):
504
  gr.Markdown("### ⚙️ 1. Global API Configuration")
 
506
  key_input = gr.Textbox(label="API Key", type="password", placeholder="sk-...", scale=1)
507
  api_base_url_input = gr.Textbox(label="Base URL (Optional)", placeholder="https://api.example.com", scale=1)
508
  key_btn = gr.Button("💾 Save API Configuration")
509
+ api_status = gr.Textbox(show_label=False, interactive=False, elem_classes="status-text")
510
+
 
 
 
511
  # 2. Document Parsing
512
  with gr.Group(elem_classes="gradio-group"):
513
  gr.Markdown("### 📄 2. Document Parsing")
514
+ pdf_input = gr.File(label="Upload Document", file_types=[".pdf"], elem_id="pdf-upload-box")
 
 
 
 
 
 
 
515
  parse_btn = gr.Button("🚀 Start Mineru & DAG Extraction", elem_classes="primary-action-btn", interactive=False)
516
+ parse_status = gr.Textbox(show_label=False, interactive=False, elem_classes="status-text")
 
 
 
517
 
518
  # 3. Asset Generation
519
  with gr.Group(elem_classes="gradio-group"):
520
  gr.Markdown("### 🎯 3. Asset Generation")
 
 
521
  with gr.Row(elem_classes="action-row"):
 
522
  gen_ppt_btn = gr.Button("📊 Gen PPT", elem_classes="action-btn", interactive=False)
523
  gen_poster_btn = gr.Button("🖼️ Gen Poster", elem_classes="action-btn", interactive=False)
524
  gen_pr_btn = gr.Button("📰 Gen PR", elem_classes="action-btn", interactive=False)
 
 
525
  gen_all_btn = gr.Button("✨ Generate All Assets (ALL)", elem_classes="primary-action-btn", interactive=False)
526
 
 
527
  with gr.Column(scale=1):
 
528
  # 4. Results & Downloads
529
  with gr.Group(elem_classes="gradio-group"):
530
  gr.Markdown("### 📦 Generation Results & Download")
531
+ gen_status = gr.Textbox(show_label=False, interactive=False, elem_classes="status-text")
 
 
532
  download_file = gr.File(label="📥 Get Final Zip Archive", interactive=False, visible=False)
533
 
534
+ # 5. Debugging
535
  with gr.Group(elem_classes="gradio-group"):
536
  gr.Markdown("### 🛠️ Developer Monitoring (Debug Only)")
537
  with gr.Tabs():
538
  with gr.Tab("📜 Terminal Stream"):
539
+ cmd_logs = gr.Textbox(show_label=False, lines=14, interactive=False, elem_classes="log-box")
 
 
 
540
  with gr.Tab("🔍 System Snapshot"):
541
  refresh_btn = gr.Button("🔄 Refresh Directory Tree")
542
+ debug_view = gr.HTML()
543
+
544
+ # 初始化加载空的 Debug 信息
545
+ demo.load(fn=get_debug_info, inputs=session_id_state, outputs=debug_view)
546
 
547
  # ================= LOGIC BINDINGS =================
548
 
 
549
  key_btn.click(
550
  fn=save_api_settings,
551
+ inputs=[key_input, api_base_url_input, session_id_state],
552
+ outputs=[api_status, debug_view, api_saved_state, user_api_key_state, user_api_base_state]
553
  )
554
 
555
+ pdf_input.upload(
556
+ fn=save_pdf,
557
+ inputs=[pdf_input, session_id_state],
558
+ outputs=[parse_status, debug_view, pdf_ready_state]
559
+ )
560
 
 
561
  pdf_input.clear(
562
  fn=clear_pdf,
563
+ inputs=[session_id_state],
564
  outputs=[parse_status, debug_view, pdf_ready_state, gen_ppt_btn, gen_poster_btn, gen_pr_btn, gen_all_btn]
565
  )
566
 
 
567
  def check_parse_btn_ready(api_ready, pdf_ready):
568
  return gr.update(interactive=(api_ready and pdf_ready))
569
 
570
  api_saved_state.change(fn=check_parse_btn_ready, inputs=[api_saved_state, pdf_ready_state], outputs=parse_btn)
571
  pdf_ready_state.change(fn=check_parse_btn_ready, inputs=[api_saved_state, pdf_ready_state], outputs=parse_btn)
572
 
 
573
  parse_btn.click(
574
  fn=run_mineru_parsing_and_dag_gen,
575
+ inputs=[session_id_state, user_api_key_state, user_api_base_state],
576
+ outputs=[parse_status, debug_view, cmd_logs, gen_ppt_btn, gen_poster_btn, gen_pr_btn, gen_all_btn]
577
  )
578
 
579
+ def trigger_gen_ppt(sid, ak, ab, progress=gr.Progress()): yield from run_final_generation("ppt", sid, ak, ab, progress)
580
+ def trigger_gen_poster(sid, ak, ab, progress=gr.Progress()): yield from run_final_generation("poster", sid, ak, ab, progress)
581
+ def trigger_gen_pr(sid, ak, ab, progress=gr.Progress()): yield from run_final_generation("pr", sid, ak, ab, progress)
582
+ def trigger_gen_all(sid, ak, ab, progress=gr.Progress()): yield from run_final_generation("all", sid, ak, ab, progress)
583
+
584
+ # 绑定需要传递所有专属状态
585
+ gen_ppt_btn.click(fn=trigger_gen_ppt, inputs=[session_id_state, user_api_key_state, user_api_base_state], outputs=[gen_status, debug_view, cmd_logs, download_file])
586
+ gen_poster_btn.click(fn=trigger_gen_poster, inputs=[session_id_state, user_api_key_state, user_api_base_state], outputs=[gen_status, debug_view, cmd_logs, download_file])
587
+ gen_pr_btn.click(fn=trigger_gen_pr, inputs=[session_id_state, user_api_key_state, user_api_base_state], outputs=[gen_status, debug_view, cmd_logs, download_file])
588
+ gen_all_btn.click(fn=trigger_gen_all, inputs=[session_id_state, user_api_key_state, user_api_base_state], outputs=[gen_status, debug_view, cmd_logs, download_file])
 
589
 
590
+ refresh_btn.click(fn=get_debug_info, inputs=[session_id_state], outputs=debug_view)
591
 
592
  if __name__ == "__main__":
593
  demo.launch()