aaron1141 commited on
Commit
bafa4e5
ยท
1 Parent(s): 9f4b8e6

fix: remove generator/queue, add api_name, add startup logs to diagnose No API Found

Browse files
Files changed (1) hide show
  1. app.py +93 -104
app.py CHANGED
@@ -8,51 +8,55 @@ import traceback
8
 
9
  import gradio as gr
10
 
11
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
 
 
12
 
 
 
 
 
 
 
13
 
14
  def run_vqa_extraction(
15
  pdf_files,
16
- task_name: str,
17
- api_url: str,
18
- llm_api_key: str,
19
- mineru_api_key: str,
20
- model_name: str,
21
- max_workers: int,
22
  ):
23
- """Generator function โ€” yields (file, status) so the UI stays alive during long runs."""
24
-
25
- def err(msg):
26
- return None, f"โŒ {msg}"
27
-
28
- # โ”€โ”€ Input validation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
29
- if not pdf_files:
30
- yield err("่ฏท่‡ณๅฐ‘ไธŠไผ ไธ€ไธช PDF ๆ–‡ไปถใ€‚"); return
31
- if not llm_api_key.strip():
32
- yield err("่ฏทๅกซๅ†™ LLM API Key๏ผˆ็”จไบŽ่ฐƒ็”จๅคงๆจกๅž‹ๆๅ– QA๏ผ‰ใ€‚"); return
33
- if not mineru_api_key.strip():
34
- yield err("่ฏทๅกซๅ†™ MinerU API Key๏ผˆ็”จไบŽ่งฃๆž PDF๏ผŒไธŽ LLM Key ไธๅŒ๏ผ‰ใ€‚"); return
35
- if not task_name.strip():
36
- task_name = "task1"
37
-
38
- yield None, "โณ [1/5] ่ฎพ็ฝฎ็Žฏๅขƒๅ˜้‡โ€ฆ"
39
-
40
- os.environ["DF_API_KEY"] = llm_api_key.strip()
41
- os.environ["MINERU_API_KEY"] = mineru_api_key.strip()
42
-
43
- workspace = tempfile.mkdtemp(prefix="dataflow_vqa_")
44
- cache_dir = os.path.join(workspace, "cache")
45
  os.makedirs(cache_dir, exist_ok=True)
46
  original_cwd = os.getcwd()
47
 
48
  try:
49
  os.chdir(workspace)
50
 
51
- # โ”€โ”€ Copy uploaded PDFs โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
52
- yield None, "โณ [2/5] ๆ•ด็†ไธŠไผ ็š„ PDF ๆ–‡ไปถโ€ฆ"
 
 
53
  pdf_paths = []
54
  for i, f in enumerate(pdf_files):
55
- src = f if isinstance(f, str) else (f.name if hasattr(f, "name") else str(f))
 
56
  dst = os.path.join(workspace, f"input_{i}.pdf")
57
  shutil.copy(src, dst)
58
  pdf_paths.append(dst)
@@ -61,58 +65,62 @@ def run_vqa_extraction(
61
  with open(input_jsonl, "w") as fout:
62
  entry = {
63
  "input_pdf_paths": pdf_paths if len(pdf_paths) > 1 else pdf_paths[0],
64
- "name": task_name.strip(),
65
  }
66
  fout.write(json.dumps(entry, ensure_ascii=False) + "\n")
67
 
68
- # โ”€โ”€ Import & initialise pipeline โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
69
- yield None, "โณ [3/5] ๅŠ ่ฝฝ Pipeline ๆจกๅ—๏ผˆ้ฆ–ๆฌกๅฏ่ƒฝ้œ€่ฆ 10โ€“30 ็ง’๏ผ‰โ€ฆ"
70
  try:
71
- from pipelines.vqa_extract_optimized_pipeline import PDF_VQA_extract_optimized_pipeline
 
 
72
  except Exception:
73
- tb = traceback.format_exc()
74
- yield err(f"ๅฏผๅ…ฅ Pipeline ๅคฑ่ดฅ๏ผŒ่ฏทๆฃ€ๆŸฅไพ่ต–ๅฎ‰่ฃ…๏ผš\n{tb}"); return
75
 
 
76
  try:
77
  pipeline = PDF_VQA_extract_optimized_pipeline(
78
- input_file = input_jsonl,
79
- api_url = api_url.rstrip("/"),
80
- model_name = model_name,
81
- max_workers = int(max_workers),
82
  )
83
  pipeline.compile()
84
  except ValueError as e:
85
  msg = str(e)
86
  if "DF_API_KEY" in msg:
87
- yield err("LLM API Key ๆœช่ƒฝ่ฏปๅ–๏ผŒ่ฏท้‡ๆ–ฐๅกซๅ†™ๅŽ็‚นๅ‡ป่ฟ่กŒใ€‚"); return
88
  if "MINERU_API_KEY" in msg:
89
- yield err("MinerU API Key ๆœช่ƒฝ่ฏปๅ–๏ผŒ่ฏท้‡ๆ–ฐๅกซๅ†™ๅŽ็‚นๅ‡ป่ฟ่กŒใ€‚"); return
90
- yield err(f"Pipeline ๅˆๅง‹ๅŒ–ๅคฑ่ดฅ๏ผš{msg}"); return
91
 
92
- # โ”€โ”€ Run pipeline โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
93
- yield None, "โณ [4/5] ่ฐƒ็”จ MinerU ่งฃๆž PDF + LLM ๆๅ– QA๏ผˆๅฏ่ƒฝ้œ€่ฆๆ•ฐๅˆ†้’Ÿ๏ผŒ่ฏท่€ๅฟƒ็ญ‰ๅพ…๏ผ‰โ€ฆ"
94
  try:
95
  pipeline.forward()
96
  except RuntimeError as e:
97
  msg = str(e)
98
  if "no api found" in msg.lower() or "Apply upload urls failed" in msg:
99
- yield err(
100
- "MinerU API Key ๆ— ๆ•ˆๆˆ–ๅทฒ่ฟ‡ๆœŸใ€‚\n"
101
- "่ฏทๅˆฐ https://mineru.net/apiManage/token ้‡ๆ–ฐ็”ณ่ฏท๏ผŒๆณจๆ„ไธŽ LLM Key ๆ˜ฏไธคไธชไธๅŒ็š„่ดฆๅทไฝ“็ณปใ€‚\n\n"
102
  f"ๅŽŸๅง‹้”™่ฏฏ๏ผš{msg}"
103
- ); return
104
  if "Cannot connect to LLM server" in msg:
105
- yield err(f"ๆ— ๆณ•่ฟžๆŽฅ LLM API๏ผŒ่ฏทๆฃ€ๆŸฅ API Base URL ๆ˜ฏๅฆๆญฃ็กฎใ€‚\n\nๅŽŸๅง‹้”™่ฏฏ๏ผš{msg}"); return
106
- yield err(f"Pipeline ่ฟ่กŒๅ‡บ้”™๏ผš{msg}"); return
107
-
108
- # โ”€โ”€ Collect output โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
109
- yield None, "โณ [5/5] ๆ•ด็†่พ“ๅ‡บ็ป“ๆžœโ€ฆ"
110
-
111
- step_files = [f for f in os.listdir(cache_dir) if re.match(r"vqa_step\d+\.jsonl", f)]
 
112
  if not step_files:
113
- yield err("Pipeline ๅฎŒๆˆไฝ†ๆœชๆ‰พๅˆฐ่พ“ๅ‡บๆ–‡ไปถ๏ผŒ่ฏทๆฃ€ๆŸฅ HF Space ๆ—ฅๅฟ—ใ€‚"); return
114
 
115
- max_step = max(int(re.findall(r"vqa_step(\d+)\.jsonl", f)[0]) for f in step_files)
 
 
 
116
  max_step_file = os.path.join(cache_dir, f"vqa_step{max_step}.jsonl")
117
 
118
  result_file = os.path.join(workspace, "raw_vqa.jsonl")
@@ -129,108 +137,89 @@ def run_vqa_extraction(
129
  f_out.write(json.dumps(out, ensure_ascii=False) + "\n")
130
  count += 1
131
 
132
- yield result_file, f"โœ… ๅฎŒๆˆ๏ผๅ…ฑๆๅ– {count} ๆก QA ๅฏน๏ผŒ็‚นๅ‡ปไธŠๆ–นไธ‹่ฝฝ raw_vqa.jsonlใ€‚"
133
 
134
  except Exception:
135
- tb = traceback.format_exc()
136
- yield err(f"ๆœช็Ÿฅ้”™่ฏฏ๏ผš\n{tb}")
137
  finally:
138
  os.chdir(original_cwd)
139
 
140
 
141
- # โ”€โ”€ Gradio UI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
142
 
143
  with gr.Blocks(title="DataFlow-VQA ยท PDF ๆๅ– Demo", theme=gr.themes.Soft()) as demo:
144
  gr.Markdown(
145
  """
146
  # ๐Ÿ”ฌ DataFlow-VQA โ€” ไปŽ PDF ๆๅ– VQA ๆ•ฐๆฎ
147
 
148
- ไธŠไผ ๆ•™ๆๆˆ–่ฏ•ๅท PDF๏ผŒ่‡ชๅŠจ็”จ [MinerU](https://mineru.net) ่งฃๆž็‰ˆ้ขใ€ๅ†็”จ LLM ๆๅ–็ป“ๆž„ๅŒ– QA ๅฏน๏ผŒ่พ“ๅ‡บ `raw_vqa.jsonl`ใ€‚
149
 
150
- **ๆต็จ‹๏ผš** PDF ๅˆๅนถ โ†’ MinerU ่งฃๆž โ†’ LLM ๆๅ– QA โ†’ ๅŽๅค„็†่พ“ๅ‡บ
151
 
152
  > ๆ‰€ๆœ‰ API ่ฐƒ็”จๅ‡้€š่ฟ‡ๆ‚จๆไพ›็š„ๅฏ†้’ฅๅฎŒๆˆ๏ผŒๆœฌ Space ไธๅญ˜ๅ‚จไปปไฝ•ๆ•ฐๆฎๆˆ–ๅฏ†้’ฅใ€‚
153
  """
154
  )
155
 
156
  with gr.Row():
157
- with gr.Column(scale=1):
158
  gr.Markdown("### ๐Ÿ“„ ไธŠไผ  PDF")
159
  pdf_files = gr.File(
160
- label="ไธŠไผ  PDF๏ผˆๅ•ๆ–‡ไปถ๏ผš้ข˜็ญ”ๆททๆŽ’๏ผ›ไธคๆ–‡ไปถ๏ผš็ฌฌ1ไธชไธบ้ข˜๏ผŒ็ฌฌ2ไธชไธบ็ญ”ๆกˆ๏ผ‰",
161
  file_types=[".pdf"],
162
  file_count="multiple",
163
  )
164
- task_name = gr.Textbox(
165
- label="ไปปๅŠกๅ็งฐ๏ผˆ็”จไบŽ็›ฎๅฝ•ๅ‘ฝๅ๏ผ‰",
166
- value="task1",
167
- placeholder="task1",
168
- )
169
 
170
- gr.Markdown("### โš™๏ธ LLM API ้…็ฝฎ")
171
  api_url = gr.Textbox(
172
  label="API Base URL",
173
  value="https://generativelanguage.googleapis.com/v1beta/openai/",
174
- placeholder="https://api.openai.com/v1",
175
  )
176
  llm_api_key = gr.Textbox(
177
  label="LLM API Key๏ผˆDF_API_KEY๏ผ‰",
178
- placeholder="sk-... / AIzaSy...",
179
  type="password",
 
180
  )
181
  model_name = gr.Textbox(
182
- label="ๆจกๅž‹ๅ็งฐ๏ผˆๆŽจ่ๅผบๆŽจ็†ๆจกๅž‹๏ผ‰",
183
  value="gemini-2.5-pro",
184
- placeholder="gemini-2.5-pro / gpt-4o / deepseek-r1",
185
  )
186
 
187
- gr.Markdown("### ๐Ÿ—๏ธ MinerU API ้…็ฝฎ")
188
  mineru_api_key = gr.Textbox(
189
  label="MinerU API Key๏ผˆMINERU_API_KEY๏ผ‰",
190
- placeholder="sk2-...",
191
  type="password",
192
- info="โš ๏ธ ็‹ฌ็ซ‹ไบŽ LLM ็š„็ฌฌไบŒไธช Key๏ผŒ็”จไบŽ PDF ่งฃๆžใ€‚ๅŽป https://mineru.net/apiManage/token ๅ…่ดน็”ณ่ฏท",
193
- )
194
- max_workers = gr.Slider(
195
- label="ๅนถๅ‘ Worker ๆ•ฐ",
196
- minimum=1, maximum=30, value=5, step=1,
197
  )
198
- run_btn = gr.Button("โ–ถ ๅผ€ๅง‹ๆๅ–", variant="primary", size="lg")
 
199
 
200
- with gr.Column(scale=1):
201
  gr.Markdown("### ๐Ÿ“ค ่พ“ๅ‡บ")
202
  status_box = gr.Textbox(
203
  label="่ฟ่กŒ็Šถๆ€",
204
  interactive=False,
205
  lines=10,
206
- placeholder="็‚นๅ‡ปใ€Œๅผ€ๅง‹ๆๅ–ใ€ๅŽ๏ผŒ่ฟ›ๅบฆไผšๅฎžๆ—ถๆ˜พ็คบๅœจ่ฟ™้‡Œโ€ฆ",
207
- )
208
- output_file = gr.File(
209
- label="ไธ‹่ฝฝๆๅ–็ป“ๆžœ๏ผˆraw_vqa.jsonl๏ผ‰",
210
- interactive=False,
211
  )
 
212
 
213
- gr.Markdown(
214
- """
215
  ---
216
- **ไธคไธช API Key ็š„ๅŒบๅˆซ๏ผš**
217
-
218
  | Key | ็”จ้€” | ็”ณ่ฏทๅœฐๅ€ |
219
  |-----|------|---------|
220
- | LLM API Key | ่ฐƒ็”จ GPT / Gemini / DeepSeek ็ญ‰ๅคงๆจกๅž‹ๆๅ– QA | ๅฏนๅบ” LLM ๆœๅŠกๅ•† |
221
- | **MinerU API Key** | ่ฐƒ็”จ MinerU ๆœๅŠก่งฃๆž PDF ็‰ˆ้ข๏ผˆๅฎŒๅ…จ็‹ฌ็ซ‹๏ผ‰ | [mineru.net/apiManage/token](https://mineru.net/apiManage/token) |
222
-
223
- **้กน็›ฎๅœฐๅ€**๏ผš[OpenDCAI/DataFlow-VQA](https://github.com/OpenDCAI/DataFlow-VQA)
224
- """
225
- )
226
 
227
  run_btn.click(
228
  fn=run_vqa_extraction,
229
  inputs=[pdf_files, task_name, api_url, llm_api_key, mineru_api_key, model_name, max_workers],
230
  outputs=[output_file, status_box],
 
 
231
  )
232
 
233
- demo.queue()
234
-
235
  if __name__ == "__main__":
236
  demo.launch()
 
8
 
9
  import gradio as gr
10
 
11
+ # Ensure repo root is on the Python path so local packages resolve correctly
12
+ _REPO_ROOT = os.path.dirname(os.path.abspath(__file__))
13
+ sys.path.insert(0, _REPO_ROOT)
14
 
15
+ print(f"[startup] repo root: {_REPO_ROOT}", flush=True)
16
+ print(f"[startup] Python: {sys.version}", flush=True)
17
+ print(f"[startup] Gradio: {gr.__version__}", flush=True)
18
+
19
+
20
+ # โ”€โ”€ Backend function โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
21
 
22
  def run_vqa_extraction(
23
  pdf_files,
24
+ task_name,
25
+ api_url,
26
+ llm_api_key,
27
+ mineru_api_key,
28
+ model_name,
29
+ max_workers,
30
  ):
31
+ # โ”€โ”€ validation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
32
+ if pdf_files is None or (isinstance(pdf_files, list) and len(pdf_files) == 0):
33
+ return None, "โŒ ่ฏท่‡ณๅฐ‘ไธŠไผ ไธ€ไธช PDF ๆ–‡ไปถใ€‚"
34
+ if not str(llm_api_key).strip():
35
+ return None, "โŒ ่ฏทๅกซๅ†™ LLM API Key๏ผˆ็”จไบŽ่ฐƒ็”จๅคงๆจกๅž‹ๆๅ– QA๏ผ‰ใ€‚"
36
+ if not str(mineru_api_key).strip():
37
+ return None, "โŒ ่ฏทๅกซๅ†™ MinerU API Key๏ผˆไธŽ LLM Key ๅฎŒๅ…จไธๅŒ๏ผŒๅŽป https://mineru.net/apiManage/token ็”ณ่ฏท๏ผ‰ใ€‚"
38
+ task_name = str(task_name).strip() or "task1"
39
+
40
+ # โ”€โ”€ env vars โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
41
+ os.environ["DF_API_KEY"] = str(llm_api_key).strip()
42
+ os.environ["MINERU_API_KEY"] = str(mineru_api_key).strip()
43
+
44
+ workspace = tempfile.mkdtemp(prefix="dataflow_vqa_")
45
+ cache_dir = os.path.join(workspace, "cache")
 
 
 
 
 
 
 
46
  os.makedirs(cache_dir, exist_ok=True)
47
  original_cwd = os.getcwd()
48
 
49
  try:
50
  os.chdir(workspace)
51
 
52
+ # โ”€โ”€ copy uploaded PDFs โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
53
+ if not isinstance(pdf_files, list):
54
+ pdf_files = [pdf_files]
55
+
56
  pdf_paths = []
57
  for i, f in enumerate(pdf_files):
58
+ src = f if isinstance(f, str) else (
59
+ f.name if hasattr(f, "name") else str(f))
60
  dst = os.path.join(workspace, f"input_{i}.pdf")
61
  shutil.copy(src, dst)
62
  pdf_paths.append(dst)
 
65
  with open(input_jsonl, "w") as fout:
66
  entry = {
67
  "input_pdf_paths": pdf_paths if len(pdf_paths) > 1 else pdf_paths[0],
68
+ "name": task_name,
69
  }
70
  fout.write(json.dumps(entry, ensure_ascii=False) + "\n")
71
 
72
+ # โ”€โ”€ import pipeline (lazy so startup stays fast) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
73
  try:
74
+ from pipelines.vqa_extract_optimized_pipeline import (
75
+ PDF_VQA_extract_optimized_pipeline,
76
+ )
77
  except Exception:
78
+ return None, f"โŒ ๅฏผๅ…ฅ Pipeline ๅคฑ่ดฅ๏ผˆไพ่ต–ๆœชๅฎ‰่ฃ…๏ผŸ๏ผ‰๏ผš\n{traceback.format_exc()}"
 
79
 
80
+ # โ”€โ”€ build pipeline โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
81
  try:
82
  pipeline = PDF_VQA_extract_optimized_pipeline(
83
+ input_file = input_jsonl,
84
+ api_url = str(api_url).rstrip("/"),
85
+ model_name = str(model_name),
86
+ max_workers = int(max_workers),
87
  )
88
  pipeline.compile()
89
  except ValueError as e:
90
  msg = str(e)
91
  if "DF_API_KEY" in msg:
92
+ return None, "โŒ LLM API Key ๆœช่ƒฝ่ฏปๅ–๏ผŒ่ฏท็กฎ่ฎคๅกซๅ†™ๅŽ้‡่ฏ•ใ€‚"
93
  if "MINERU_API_KEY" in msg:
94
+ return None, "โŒ MinerU API Key ๆœช่ƒฝ่ฏปๅ–๏ผŒ่ฏท็กฎ่ฎคๅกซๅ†™ๅŽ้‡่ฏ•ใ€‚"
95
+ return None, f"โŒ Pipeline ๅˆๅง‹ๅŒ–ๅคฑ่ดฅ๏ผš{msg}"
96
 
97
+ # โ”€โ”€ run (this blocks until all steps complete) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
98
  try:
99
  pipeline.forward()
100
  except RuntimeError as e:
101
  msg = str(e)
102
  if "no api found" in msg.lower() or "Apply upload urls failed" in msg:
103
+ return None, (
104
+ "โŒ MinerU API Key ๆ— ๆ•ˆๆˆ–ๅทฒ่ฟ‡ๆœŸใ€‚\n"
105
+ "่ฏทๅˆฐ https://mineru.net/apiManage/token ้‡ๆ–ฐ็”ณ่ฏทใ€‚\n\n"
106
  f"ๅŽŸๅง‹้”™่ฏฏ๏ผš{msg}"
107
+ )
108
  if "Cannot connect to LLM server" in msg:
109
+ return None, f"โŒ ๆ— ๆณ•่ฟžๆŽฅ LLM API๏ผŒ่ฏทๆฃ€ๆŸฅ Base URLใ€‚\n\nๅŽŸๅง‹้”™่ฏฏ๏ผš{msg}"
110
+ return None, f"โŒ Pipeline ่ฟ่กŒๅ‡บ้”™๏ผš{msg}"
111
+
112
+ # โ”€โ”€ collect output โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
113
+ step_files = [
114
+ f for f in os.listdir(cache_dir)
115
+ if re.match(r"vqa_step\d+\.jsonl", f)
116
+ ]
117
  if not step_files:
118
+ return None, "โŒ Pipeline ๅฎŒๆˆไฝ†ๆœชๆ‰พๅˆฐ่พ“ๅ‡บๆ–‡ไปถ๏ผŒ่ฏทๆฃ€ๆŸฅ HF Space ็š„ Runtime ๆ—ฅๅฟ—ใ€‚"
119
 
120
+ max_step = max(
121
+ int(re.findall(r"vqa_step(\d+)\.jsonl", f)[0])
122
+ for f in step_files
123
+ )
124
  max_step_file = os.path.join(cache_dir, f"vqa_step{max_step}.jsonl")
125
 
126
  result_file = os.path.join(workspace, "raw_vqa.jsonl")
 
137
  f_out.write(json.dumps(out, ensure_ascii=False) + "\n")
138
  count += 1
139
 
140
+ return result_file, f"โœ… ๅฎŒๆˆ๏ผๅ…ฑๆๅ– {count} ๆก QA ๅฏน๏ผŒ็‚นๅ‡ปไธŠๆ–นไธ‹่ฝฝ raw_vqa.jsonlใ€‚"
141
 
142
  except Exception:
143
+ return None, f"โŒ ๆœช็Ÿฅ้”™่ฏฏ๏ผš\n{traceback.format_exc()}"
 
144
  finally:
145
  os.chdir(original_cwd)
146
 
147
 
148
+ # โ”€โ”€ UI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
149
 
150
  with gr.Blocks(title="DataFlow-VQA ยท PDF ๆๅ– Demo", theme=gr.themes.Soft()) as demo:
151
  gr.Markdown(
152
  """
153
  # ๐Ÿ”ฌ DataFlow-VQA โ€” ไปŽ PDF ๆๅ– VQA ๆ•ฐๆฎ
154
 
155
+ ไธŠไผ ๆ•™ๆๆˆ–่ฏ•ๅท PDF๏ผŒ็”จ [MinerU](https://mineru.net) ่งฃๆž็‰ˆ้ขใ€ๅ†็”จ LLM ๆๅ–็ป“ๆž„ๅŒ– QA ๅฏน๏ผŒ่พ“ๅ‡บ `raw_vqa.jsonl`ใ€‚
156
 
157
+ **ๆต็จ‹๏ผš** PDF ไธŠไผ  โ†’ MinerU ่งฃๆž โ†’ LLM ๆๅ– QA โ†’ ไธ‹่ฝฝ็ป“ๆžœ
158
 
159
  > ๆ‰€ๆœ‰ API ่ฐƒ็”จๅ‡้€š่ฟ‡ๆ‚จๆไพ›็š„ๅฏ†้’ฅๅฎŒๆˆ๏ผŒๆœฌ Space ไธๅญ˜ๅ‚จไปปไฝ•ๆ•ฐๆฎๆˆ–ๅฏ†้’ฅใ€‚
160
  """
161
  )
162
 
163
  with gr.Row():
164
+ with gr.Column():
165
  gr.Markdown("### ๐Ÿ“„ ไธŠไผ  PDF")
166
  pdf_files = gr.File(
167
+ label="ไธŠไผ  PDF๏ผˆ1 ไธช๏ผš้ข˜็ญ”ๆททๆŽ’๏ผ›2 ไธช๏ผš็ฌฌ1้ข˜็›ฎ็ฌฌ2็ญ”ๆกˆ๏ผ‰",
168
  file_types=[".pdf"],
169
  file_count="multiple",
170
  )
171
+ task_name = gr.Textbox(label="ไปปๅŠกๅ็งฐ", value="task1")
 
 
 
 
172
 
173
+ gr.Markdown("### โš™๏ธ LLM ้…็ฝฎ")
174
  api_url = gr.Textbox(
175
  label="API Base URL",
176
  value="https://generativelanguage.googleapis.com/v1beta/openai/",
 
177
  )
178
  llm_api_key = gr.Textbox(
179
  label="LLM API Key๏ผˆDF_API_KEY๏ผ‰",
 
180
  type="password",
181
+ placeholder="sk-... / AIzaSy...",
182
  )
183
  model_name = gr.Textbox(
184
+ label="ๆจกๅž‹ๅ็งฐ",
185
  value="gemini-2.5-pro",
 
186
  )
187
 
188
+ gr.Markdown("### ๐Ÿ—๏ธ MinerU ้…็ฝฎ")
189
  mineru_api_key = gr.Textbox(
190
  label="MinerU API Key๏ผˆMINERU_API_KEY๏ผ‰",
 
191
  type="password",
192
+ placeholder="sk2-...",
193
+ info="็‹ฌ็ซ‹ไบŽ LLM ็š„ Key๏ผŒๅŽป https://mineru.net/apiManage/token ๅ…่ดน็”ณ่ฏท",
 
 
 
194
  )
195
+ max_workers = gr.Slider(label="ๅนถๅ‘ๆ•ฐ", minimum=1, maximum=30, value=5, step=1)
196
+ run_btn = gr.Button("โ–ถ ๅผ€ๅง‹ๆๅ–", variant="primary")
197
 
198
+ with gr.Column():
199
  gr.Markdown("### ๐Ÿ“ค ่พ“ๅ‡บ")
200
  status_box = gr.Textbox(
201
  label="่ฟ่กŒ็Šถๆ€",
202
  interactive=False,
203
  lines=10,
204
+ placeholder="็‚นๅ‡ปใ€Œๅผ€ๅง‹ๆๅ–ใ€ๅŽ็Šถๆ€ๆ˜พ็คบๅœจ่ฟ™้‡Œ๏ผˆ่ฟ่กŒ้œ€ๆ•ฐๅˆ†้’Ÿ๏ผŒ่ฏท่€ๅฟƒ็ญ‰ๅพ…๏ผ‰โ€ฆ",
 
 
 
 
205
  )
206
+ output_file = gr.File(label="ไธ‹่ฝฝ็ป“ๆžœ๏ผˆraw_vqa.jsonl๏ผ‰", interactive=False)
207
 
208
+ gr.Markdown("""
 
209
  ---
 
 
210
  | Key | ็”จ้€” | ็”ณ่ฏทๅœฐๅ€ |
211
  |-----|------|---------|
212
+ | LLM API Key | ่ฐƒ็”จ GPT/Gemini ็ญ‰ๅคงๆจกๅž‹ๆๅ– QA | ๅฏนๅบ” LLM ๆœๅŠกๅ•† |
213
+ | **MinerU API Key** | ่งฃๆž PDF ็‰ˆ้ข๏ผˆไธŽ LLM ๅฎŒๅ…จ็‹ฌ็ซ‹๏ผ‰ | [mineru.net/apiManage/token](https://mineru.net/apiManage/token) |
214
+ """)
 
 
 
215
 
216
  run_btn.click(
217
  fn=run_vqa_extraction,
218
  inputs=[pdf_files, task_name, api_url, llm_api_key, mineru_api_key, model_name, max_workers],
219
  outputs=[output_file, status_box],
220
+ api_name="run_vqa_extraction",
221
+ show_progress="full",
222
  )
223
 
 
 
224
  if __name__ == "__main__":
225
  demo.launch()