JaceWei commited on
Commit
136148e
Β·
1 Parent(s): 73ee809
Files changed (1) hide show
  1. app.py +111 -33
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import gradio as gr
2
- import subprocess, shutil, os, zipfile, datetime
3
  from pathlib import Path
 
4
  def _ensure_versions():
5
  import importlib, subprocess, sys
6
  def ver(pkg):
@@ -19,7 +20,7 @@ def _ensure_versions():
19
  pass
20
 
21
  if not hub_ok:
22
- subprocess.check_call([sys.executable, "-m", "pip",
23
  "install", "huggingface-hub==0.27.1",
24
  "transformers==4.48.0",
25
  "--force-reinstall", "--no-deps"])
@@ -32,6 +33,7 @@ LOGO_DIR = INPUT_DIR / "logo"
32
  POSTER_LATEX_DIR = ROOT / "posterbuilder" / "latex_proj"
33
  ZIP_PATH = ROOT / "output.zip"
34
  LOG_PATH = ROOT / "last_run.log"
 
35
 
36
  def run_pipeline(arxiv_url, pdf_file, openai_key, logo_files):
37
  start_time = datetime.datetime.now()
@@ -50,6 +52,8 @@ def run_pipeline(arxiv_url, pdf_file, openai_key, logo_files):
50
  if ZIP_PATH.exists():
51
  ZIP_PATH.unlink()
52
  logs.append("🧹 Cleaned previous output.\n")
 
 
53
 
54
  # ====== Validation: must upload LOGO ======
55
  # Gradio may return a single file or a list, normalize to list
@@ -63,7 +67,8 @@ def run_pipeline(arxiv_url, pdf_file, openai_key, logo_files):
63
  msg = "❌ You must upload at least one institutional logo (multiple allowed)."
64
  logs.append(msg)
65
  _write_logs(logs)
66
- return "\n".join(logs), None
 
67
 
68
  # Clear input/logo and then save new files
69
  for item in LOGO_DIR.iterdir():
@@ -75,6 +80,8 @@ def run_pipeline(arxiv_url, pdf_file, openai_key, logo_files):
75
  shutil.copy(lf.name, p)
76
  saved_logo_paths.append(p)
77
  logs.append(f"🏷️ Saved {len(saved_logo_paths)} logo file(s) to: {LOGO_DIR}\n")
 
 
78
 
79
  # ====== Handle uploaded PDF (optional) ======
80
  pdf_path = None
@@ -88,13 +95,16 @@ def run_pipeline(arxiv_url, pdf_file, openai_key, logo_files):
88
  # For pipeline Step 1.5 compatibility: also copy to input/paper.pdf
89
  canonical_pdf = INPUT_DIR / "paper.pdf"
90
  shutil.copy(pdf_file.name, canonical_pdf)
 
 
91
 
92
  # ====== Validate input source ======
93
  if not arxiv_url and not pdf_file:
94
  msg = "❌ Please provide either an arXiv link or upload a PDF file (choose one)."
95
  logs.append(msg)
96
  _write_logs(logs)
97
- return "\n".join(logs), None
 
98
 
99
  # ====== Build command ======
100
  cmd = [
@@ -105,59 +115,127 @@ def run_pipeline(arxiv_url, pdf_file, openai_key, logo_files):
105
  "--paper_latex_root", "input/latex_proj",
106
  "--openai_key", openai_key,
107
  "--gemini_key", "##",
108
- "--logo_dir", str(LOGO_DIR) # πŸ‘ˆ Added: pass logo directory
109
  ]
110
-
111
  if arxiv_url:
112
  cmd += ["--arxiv_url", arxiv_url]
113
  # if pdf_path:
114
  # cmd += ["--pdf_path", str(pdf_path)]
115
 
 
 
 
 
 
116
  try:
117
- result = subprocess.run(
118
- cmd, capture_output=True, text=True, timeout=1800
 
 
 
 
 
119
  )
120
- logs.append("\n======= STDOUT =======\n")
121
- logs.append(result.stdout)
122
- logs.append("\n======= STDERR =======\n")
123
- logs.append(result.stderr)
124
- except subprocess.TimeoutExpired:
125
- msg = "❌ Pipeline timed out (30 min limit)."
126
  logs.append(msg)
127
  _write_logs(logs)
128
- return "\n".join(logs), None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  except Exception as e:
130
- msg = f"❌ Pipeline error: {e}"
131
- logs.append(msg)
132
  _write_logs(logs)
133
- return "\n".join(logs), None
 
 
 
 
 
 
 
134
 
135
  # ====== Check output & zip ======
136
- if not any(OUTPUT_DIR.iterdir()):
137
- msg = "❌ No output generated. Please check logs below."
 
 
 
 
 
 
 
 
138
  logs.append(msg)
139
  _write_logs(logs)
140
- return "\n".join(logs), None
 
141
 
142
- with zipfile.ZipFile(ZIP_PATH, 'w', zipfile.ZIP_DEFLATED) as zipf:
143
- for root, dirs, files in os.walk(OUTPUT_DIR):
144
- for file in files:
145
- file_path = Path(root) / file
146
- arcname = file_path.relative_to(OUTPUT_DIR)
147
- zipf.write(file_path, arcname=arcname)
148
- logs.append(f"βœ… Zipped output folder to {ZIP_PATH}\n")
 
 
 
149
 
150
  end_time = datetime.datetime.now()
151
  logs.append(f"🏁 Completed at {end_time.strftime('%Y-%m-%d %H:%M:%S')} (Duration: {(end_time - start_time).seconds}s)\n")
152
 
153
  _write_logs(logs)
154
- return "\n".join(logs), str(ZIP_PATH)
155
-
156
 
157
  def _write_logs(logs):
158
- with open(LOG_PATH, "w", encoding="utf-8") as f:
159
- f.write("\n".join(logs))
160
-
 
 
 
161
 
162
  # ===================== Gradio UI =====================
163
  iface = gr.Interface(
 
1
  import gradio as gr
2
+ import subprocess, shutil, os, zipfile, datetime, sys, time
3
  from pathlib import Path
4
+
5
  def _ensure_versions():
6
  import importlib, subprocess, sys
7
  def ver(pkg):
 
20
  pass
21
 
22
  if not hub_ok:
23
+ subprocess.check_check_call([sys.executable, "-m", "pip",
24
  "install", "huggingface-hub==0.27.1",
25
  "transformers==4.48.0",
26
  "--force-reinstall", "--no-deps"])
 
33
  POSTER_LATEX_DIR = ROOT / "posterbuilder" / "latex_proj"
34
  ZIP_PATH = ROOT / "output.zip"
35
  LOG_PATH = ROOT / "last_run.log"
36
+ TIMEOUT_SECONDS = 1800 # 30 minutes
37
 
38
  def run_pipeline(arxiv_url, pdf_file, openai_key, logo_files):
39
  start_time = datetime.datetime.now()
 
52
  if ZIP_PATH.exists():
53
  ZIP_PATH.unlink()
54
  logs.append("🧹 Cleaned previous output.\n")
55
+ _write_logs(logs)
56
+ yield "\n".join(logs), None
57
 
58
  # ====== Validation: must upload LOGO ======
59
  # Gradio may return a single file or a list, normalize to list
 
67
  msg = "❌ You must upload at least one institutional logo (multiple allowed)."
68
  logs.append(msg)
69
  _write_logs(logs)
70
+ yield "\n".join(logs), None
71
+ return
72
 
73
  # Clear input/logo and then save new files
74
  for item in LOGO_DIR.iterdir():
 
80
  shutil.copy(lf.name, p)
81
  saved_logo_paths.append(p)
82
  logs.append(f"🏷️ Saved {len(saved_logo_paths)} logo file(s) to: {LOGO_DIR}\n")
83
+ _write_logs(logs)
84
+ yield "\n".join(logs), None
85
 
86
  # ====== Handle uploaded PDF (optional) ======
87
  pdf_path = None
 
95
  # For pipeline Step 1.5 compatibility: also copy to input/paper.pdf
96
  canonical_pdf = INPUT_DIR / "paper.pdf"
97
  shutil.copy(pdf_file.name, canonical_pdf)
98
+ _write_logs(logs)
99
+ yield "\n".join(logs), None
100
 
101
  # ====== Validate input source ======
102
  if not arxiv_url and not pdf_file:
103
  msg = "❌ Please provide either an arXiv link or upload a PDF file (choose one)."
104
  logs.append(msg)
105
  _write_logs(logs)
106
+ yield "\n".join(logs), None
107
+ return
108
 
109
  # ====== Build command ======
110
  cmd = [
 
115
  "--paper_latex_root", "input/latex_proj",
116
  "--openai_key", openai_key,
117
  "--gemini_key", "##",
118
+ "--logo_dir", str(LOGO_DIR) # πŸ‘ˆ pass logo directory
119
  ]
 
120
  if arxiv_url:
121
  cmd += ["--arxiv_url", arxiv_url]
122
  # if pdf_path:
123
  # cmd += ["--pdf_path", str(pdf_path)]
124
 
125
+ # ====== Run command with REAL-TIME streaming ======
126
+ logs.append("\n======= REAL-TIME LOG =======\n")
127
+ _write_logs(logs)
128
+ yield "\n".join(logs), None
129
+
130
  try:
131
+ process = subprocess.Popen(
132
+ cmd,
133
+ stdout=subprocess.PIPE,
134
+ stderr=subprocess.STDOUT,
135
+ text=True,
136
+ bufsize=1,
137
+ universal_newlines=True,
138
  )
139
+ except Exception as e:
140
+ msg = f"❌ Pipeline failed to start: {e}"
 
 
 
 
141
  logs.append(msg)
142
  _write_logs(logs)
143
+ yield "\n".join(logs), None
144
+ return
145
+
146
+ last_yield = time.time()
147
+ try:
148
+ while True:
149
+ # Timeout guard
150
+ if (datetime.datetime.now() - start_time).total_seconds() > TIMEOUT_SECONDS:
151
+ logs.append("❌ Pipeline timed out (30 min limit). Killing process...\n")
152
+ try:
153
+ process.kill()
154
+ except Exception:
155
+ pass
156
+ _write_logs(logs)
157
+ yield "\n".join(logs), None
158
+ return
159
+
160
+ line = process.stdout.readline()
161
+ if line:
162
+ # echo to HF Space logs as well
163
+ print(line, end="")
164
+ logs.append(line.rstrip("\n"))
165
+ # write & occasionally yield to reduce UI churn
166
+ _write_logs(logs)
167
+ now = time.time()
168
+ if now - last_yield >= 0.3: # throttle UI updates ~3/sec
169
+ last_yield = now
170
+ yield "\n".join(logs), None
171
+ elif process.poll() is not None:
172
+ break
173
+ else:
174
+ time.sleep(0.05)
175
+
176
+ return_code = process.wait()
177
+ logs.append(f"\nProcess finished with code {return_code}\n")
178
+ _write_logs(logs)
179
+ yield "\n".join(logs), None
180
+
181
+ if return_code != 0:
182
+ logs.append("❌ Process exited with non-zero status. See logs above.\n")
183
+ _write_logs(logs)
184
+ yield "\n".join(logs), None
185
+ return
186
+
187
  except Exception as e:
188
+ logs.append(f"❌ Error during streaming: {e}\n")
 
189
  _write_logs(logs)
190
+ yield "\n".join(logs), None
191
+ return
192
+ finally:
193
+ try:
194
+ if process.stdout:
195
+ process.stdout.close()
196
+ except Exception:
197
+ pass
198
 
199
  # ====== Check output & zip ======
200
+ has_output = False
201
+ try:
202
+ for _ in OUTPUT_DIR.iterdir():
203
+ has_output = True
204
+ break
205
+ except FileNotFoundError:
206
+ has_output = False
207
+
208
+ if not has_output:
209
+ msg = "❌ No output generated. Please check logs above."
210
  logs.append(msg)
211
  _write_logs(logs)
212
+ yield "\n".join(logs), None
213
+ return
214
 
215
+ try:
216
+ with zipfile.ZipFile(ZIP_PATH, 'w', zipfile.ZIP_DEFLATED) as zipf:
217
+ for root, dirs, files in os.walk(OUTPUT_DIR):
218
+ for file in files:
219
+ file_path = Path(root) / file
220
+ arcname = file_path.relative_to(OUTPUT_DIR)
221
+ zipf.write(file_path, arcname=arcname)
222
+ logs.append(f"βœ… Zipped output folder to {ZIP_PATH}\n")
223
+ except Exception as e:
224
+ logs.append(f"❌ Failed to create zip: {e}\n")
225
 
226
  end_time = datetime.datetime.now()
227
  logs.append(f"🏁 Completed at {end_time.strftime('%Y-%m-%d %H:%M:%S')} (Duration: {(end_time - start_time).seconds}s)\n")
228
 
229
  _write_logs(logs)
230
+ yield "\n".join(logs), (str(ZIP_PATH) if ZIP_PATH.exists() else None)
 
231
 
232
  def _write_logs(logs):
233
+ try:
234
+ with open(LOG_PATH, "w", encoding="utf-8") as f:
235
+ f.write("\n".join(logs))
236
+ except Exception:
237
+ # Avoid crashing UI if disk I/O fails
238
+ pass
239
 
240
  # ===================== Gradio UI =====================
241
  iface = gr.Interface(