openfree commited on
Commit
3643f99
·
verified ·
1 Parent(s): 822a9a7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +123 -199
app.py CHANGED
@@ -9,29 +9,30 @@ import requests
9
  from urllib.parse import urlparse
10
  import xml.etree.ElementTree as ET
11
 
12
- # Model repository path and device selection
 
 
13
  model_path = "ssocean/NAIP"
14
  device = "cuda" if torch.cuda.is_available() else "cpu"
15
 
16
- # Global model/tokenizer variables
17
  model = None
18
  tokenizer = None
19
 
 
 
 
20
  def fetch_arxiv_paper(arxiv_input):
21
  """
22
- Fetch paper details (title, abstract) from an arXiv URL or ID using requests.
23
  """
24
  try:
25
- # If user passed a full arxiv.org link, parse out the ID
26
  if "arxiv.org" in arxiv_input:
27
  parsed = urlparse(arxiv_input)
28
  path = parsed.path
29
  arxiv_id = path.split("/")[-1].replace(".pdf", "")
30
  else:
31
- # Otherwise just use the raw ID
32
  arxiv_id = arxiv_input.strip()
33
 
34
- # ArXiv API query
35
  api_url = f"http://export.arxiv.org/api/query?id_list={arxiv_id}"
36
  resp = requests.get(api_url)
37
  if resp.status_code != 200:
@@ -39,64 +40,71 @@ def fetch_arxiv_paper(arxiv_input):
39
  "title": "",
40
  "abstract": "",
41
  "success": False,
42
- "message": "Error fetching paper from arXiv API"
43
  }
44
 
45
- # Parse XML response
46
  root = ET.fromstring(resp.text)
47
  ns = {"arxiv": "http://www.w3.org/2005/Atom"}
48
  entry = root.find(".//arxiv:entry", ns)
49
  if entry is None:
50
- return {
51
- "title": "",
52
- "abstract": "",
53
- "success": False,
54
- "message": "Paper not found"
55
- }
56
 
57
  title = entry.find("arxiv:title", ns).text.strip()
58
  abstract = entry.find("arxiv:summary", ns).text.strip()
 
59
  return {
60
  "title": title,
61
  "abstract": abstract,
62
  "success": True,
63
- "message": "Paper fetched successfully!"
64
  }
65
  except Exception as e:
66
  return {
67
  "title": "",
68
  "abstract": "",
69
  "success": False,
70
- "message": f"Error fetching paper: {e}"
71
  }
72
 
 
 
 
73
  @spaces.GPU(duration=60, enable_queue=True)
74
  def predict(title, abstract):
75
  """
76
- Predict a normalized academic impact score (0–1) given the paper title & abstract.
77
- Loads the model once globally, then uses it for inference.
78
  """
79
  global model, tokenizer
80
-
81
  if model is None:
82
- # Load model config, disable quantization, and set number of labels if needed
83
  config = AutoConfig.from_pretrained(model_path)
84
- config.quantization_config = None
85
- config.num_labels = 1 # For classification/logit output
 
 
 
 
 
 
86
 
87
- # IMPORTANT: Do not pass num_labels directly into from_pretrained for LLaMA-based models
88
- model = AutoModelForSequenceClassification.from_pretrained(
89
  model_path,
90
- config=config,
91
- torch_dtype=torch.float32, # Use full-precision float32
92
- device_map=None, # We'll move it manually
93
  low_cpu_mem_usage=False
94
  )
95
- model.to(device)
96
- model.eval()
97
 
98
- tokenizer = AutoTokenizer.from_pretrained(model_path)
 
99
 
 
 
 
 
100
  text = (
101
  f"Given a certain paper,\n"
102
  f"Title: {title.strip()}\n"
@@ -108,17 +116,20 @@ def predict(title, abstract):
108
  inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=1024)
109
  inputs = {k: v.to(device) for k, v in inputs.items()}
110
  with torch.no_grad():
111
- output = model(**inputs)
112
- logits = output.logits
113
  prob = torch.sigmoid(logits).item()
114
- score = min(1.0, prob + 0.05) # +0.05 offset, capped at 1.0
115
  return round(score, 4)
116
  except Exception as e:
117
- print(f"Prediction error: {e}")
118
- return 0.0 # Return 0 in case of any error
119
 
 
 
 
120
  def get_grade_and_emoji(score):
121
- """Convert a 0–1 score into a tier grade with emoji indicator."""
122
  if score >= 0.900: return "AAA 🌟"
123
  if score >= 0.800: return "AA ⭐"
124
  if score >= 0.650: return "A ✨"
@@ -129,9 +140,13 @@ def get_grade_and_emoji(score):
129
  if score >= 0.300: return "CC ✏️"
130
  return "C 📑"
131
 
 
 
 
132
  def validate_input(title, abstract):
133
  """
134
- Ensure title >=3 words, abstract >=50 words, and only ASCII chars.
 
135
  """
136
  non_ascii = re.compile(r"[^\x00-\x7F]")
137
  if len(title.split()) < 3:
@@ -145,90 +160,60 @@ def validate_input(title, abstract):
145
  return True, "Inputs look good."
146
 
147
  def update_button_status(title, abstract):
 
148
  valid, msg = validate_input(title, abstract)
149
  if not valid:
150
  return gr.update(value="Error: " + msg), gr.update(interactive=False)
151
  return gr.update(value=msg), gr.update(interactive=True)
152
 
 
 
 
153
  def process_arxiv_input(arxiv_input):
154
  """
155
- Helper to fill in title/abstract fields from an arXiv link/ID.
156
  """
157
  if not arxiv_input.strip():
158
  return "", "", "Please enter an arXiv URL or ID"
159
- result = fetch_arxiv_paper(arxiv_input)
160
- if result["success"]:
161
- return result["title"], result["abstract"], result["message"]
162
- return "", "", result["message"]
163
 
164
- # Custom CSS for styling
 
 
165
  css = """
166
  .gradio-container { font-family: Arial, sans-serif; }
167
  .main-title {
168
- text-align: center;
169
- color: #2563eb;
170
- font-size: 2.5rem !important;
171
- margin-bottom: 1rem !important;
172
- background: linear-gradient(45deg, #2563eb, #1d4ed8);
173
- -webkit-background-clip: text;
174
- -webkit-text-fill-color: transparent;
175
- }
176
- .sub-title {
177
- text-align: center;
178
- color: #4b5563;
179
- font-size: 1.5rem !important;
180
- margin-bottom: 2rem !important;
181
  }
182
  .input-section {
183
- background: white;
184
- padding: 2rem;
185
- border-radius: 1rem;
186
- box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
187
  }
188
  .result-section {
189
- background: #f8fafc;
190
- padding: 2rem;
191
- border-radius: 1rem;
192
- margin-top: 2rem;
193
- }
194
- .methodology-section {
195
- background: #ecfdf5;
196
- padding: 2rem;
197
- border-radius: 1rem;
198
- margin-top: 2rem;
199
- }
200
- .example-section {
201
- background: #fff7ed;
202
- padding: 2rem;
203
- border-radius: 1rem;
204
- margin-top: 2rem;
205
  }
206
  .grade-display {
207
- font-size: 3rem;
208
- text-align: center;
209
- margin: 1rem 0;
210
  }
211
  .arxiv-input {
212
- margin-bottom: 1.5rem;
213
- padding: 1rem;
214
- background: #f3f4f6;
215
- border-radius: 0.5rem;
216
  }
217
  .arxiv-link {
218
- color: #2563eb;
219
- text-decoration: underline;
220
- font-size: 0.9em;
221
- margin-top: 0.5em;
222
- }
223
- .arxiv-note {
224
- color: #666;
225
- font-size: 0.9em;
226
- margin-top: 0.5em;
227
- margin-bottom: 0.5em;
228
  }
229
  """
230
 
231
- # Example papers
 
 
232
  example_papers = [
233
  {
234
  "title": "Attention Is All You Need",
@@ -242,7 +227,7 @@ example_papers = [
242
  "parallelizable and requiring significantly less time to train."
243
  ),
244
  "score": 0.982,
245
- "note": "💫 Revolutionary paper that introduced the Transformer architecture."
246
  },
247
  {
248
  "title": "Language Models are Few-Shot Learners",
@@ -252,13 +237,13 @@ example_papers = [
252
  "typically task-agnostic in architecture, this method still requires task-specific "
253
  "fine-tuning datasets of thousands or tens of thousands of examples. By contrast, humans "
254
  "can generally perform a new language task from only a few examples or from simple "
255
- "instructions - something which current NLP systems still largely struggle to do. Here we "
256
  "show that scaling up language models greatly improves task-agnostic, few-shot "
257
  "performance, sometimes even reaching competitiveness with prior state-of-the-art "
258
  "fine-tuning approaches."
259
  ),
260
  "score": 0.956,
261
- "note": "🚀 Groundbreaking GPT-3 paper that demonstrated the power of large language models."
262
  },
263
  {
264
  "title": "An Empirical Study of Neural Network Training Protocols",
@@ -270,135 +255,74 @@ example_papers = [
270
  "insights for deep learning practitioners."
271
  ),
272
  "score": 0.623,
273
- "note": "📚 Solid research paper with useful findings but more limited scope and impact."
274
  }
275
  ]
276
 
277
- # Build Gradio interface
 
 
278
  with gr.Blocks(theme=gr.themes.Default(), css=css) as iface:
279
- gr.Markdown(
280
- """
281
- # Papers Impact: AI-Powered Research Impact Predictor
282
- ## https://discord.gg/openfreeai
283
- """
284
- )
285
- gr.HTML("""<a href="https://visitorbadge.io/status?path=https%3A%2F%2FVIDraft-PaperImpact.hf.space">
286
- <img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2FVIDraft-PaperImpact.hf.space&countColor=%23263759" />
287
- </a>""")
288
 
 
289
  with gr.Row():
290
  with gr.Column(elem_classes="input-section"):
291
- # arXiv import group
292
  with gr.Group(elem_classes="arxiv-input"):
293
- gr.Markdown("### 📑 Import from arXiv")
294
  arxiv_input = gr.Textbox(
295
  lines=1,
296
- placeholder="Enter arXiv URL or ID (e.g., 2504.11651)",
297
- label="arXiv Paper URL/ID",
298
  value="2504.11651"
299
  )
300
- gr.Markdown(
301
- """
302
- <p class="arxiv-note">
303
- Click input field to use example paper or browse papers at
304
- <a href="https://arxiv.org" target="_blank" class="arxiv-link">arxiv.org</a>
305
- </p>
306
- """
307
- )
308
- fetch_button = gr.Button("🔍 Fetch Paper Details", variant="secondary")
309
 
310
- gr.Markdown("### 📝 Or Enter Paper Details Manually")
311
  title_input = gr.Textbox(
312
  lines=2,
313
- placeholder="Enter Paper Title (minimum 3 words)...",
314
  label="Paper Title"
315
  )
316
- abstract_input = gr.Textbox(
317
  lines=5,
318
- placeholder="Enter Paper Abstract (minimum 50 words)...",
319
  label="Paper Abstract"
320
  )
321
- validation_status = gr.Textbox(label="✔️ Validation Status", interactive=False)
322
- submit_button = gr.Button("🎯 Predict Impact", interactive=False, variant="primary")
323
 
324
  with gr.Column(elem_classes="result-section"):
325
- with gr.Group():
326
- score_output = gr.Number(label="🎯 Impact Score")
327
- grade_output = gr.Textbox(label="🏆 Grade", value="", elem_classes="grade-display")
328
 
329
- with gr.Row(elem_classes="methodology-section"):
330
- gr.Markdown(
331
- """
332
- ### 🔬 Scientific Methodology
333
- - **Training Data**: Model trained on extensive dataset of published papers from CS.CV, CS.CL(NLP), and CS.AI fields
334
- - **Optimization**: NDCG optimization with Sigmoid activation and MSE loss function
335
- - **Validation**: Cross-validated against historical paper impact data
336
- - **Architecture**: Advanced transformer-based deep textual analysis
337
- - **Metrics**: Quantitative analysis of citation patterns and research influence
338
- """
339
- )
340
-
341
- with gr.Row():
342
- gr.Markdown(
343
- """
344
- ### 📊 Rating Scale
345
- | Grade | Score Range | Description | Indicator |
346
- |-------|-------------|-------------|-----------|
347
- | AAA | 0.900-1.000 | Exceptional Impact | 🌟 |
348
- | AA | 0.800-0.899 | Very High Impact | ⭐ |
349
- | A | 0.650-0.799 | High Impact | ✨ |
350
- | BBB | 0.600-0.649 | Above Average Impact | 🔵 |
351
- | BB | 0.550-0.599 | Moderate Impact | 📘 |
352
- | B | 0.500-0.549 | Average Impact | 📖 |
353
- | CCC | 0.400-0.499 | Below Average Impact | 📝 |
354
- | CC | 0.300-0.399 | Low Impact | ✏️ |
355
- | C | < 0.299 | Limited Impact | 📑 |
356
- """
357
- )
358
-
359
- with gr.Row(elem_classes="example-section"):
360
- gr.Markdown("### 📋 Example Papers")
361
- for paper in example_papers:
362
- gr.Markdown(
363
- f"""
364
- #### {paper['title']}
365
- **Score**: {paper.get('score', 'N/A')} | **Grade**: {get_grade_and_emoji(paper.get('score', 0))}
366
- {paper['abstract']}
367
- *{paper['note']}*
368
- ---
369
- """
370
- )
371
 
372
- # Validate button status on input changes
373
- title_input.change(
374
- update_button_status,
375
- inputs=[title_input, abstract_input],
376
- outputs=[validation_status, submit_button]
377
- )
378
- abstract_input.change(
379
- update_button_status,
380
- inputs=[title_input, abstract_input],
381
- outputs=[validation_status, submit_button]
382
- )
383
 
384
- # Fetch from arXiv
385
- fetch_button.click(
386
- process_arxiv_input,
387
- inputs=[arxiv_input],
388
- outputs=[title_input, abstract_input, validation_status]
389
- )
390
 
391
- # Predict callback
392
- def process_prediction(title, abstract):
393
- score = predict(title, abstract)
394
- grade = get_grade_and_emoji(score)
395
- return score, grade
396
 
397
- submit_button.click(
398
- process_prediction,
399
- inputs=[title_input, abstract_input],
400
- outputs=[score_output, grade_output]
401
- )
 
 
 
 
402
 
 
 
 
403
  if __name__ == "__main__":
404
  iface.launch()
 
9
  from urllib.parse import urlparse
10
  import xml.etree.ElementTree as ET
11
 
12
+ ##################################################
13
+ # Global setup
14
+ ##################################################
15
  model_path = "ssocean/NAIP"
16
  device = "cuda" if torch.cuda.is_available() else "cpu"
17
 
 
18
  model = None
19
  tokenizer = None
20
 
21
+ ##################################################
22
+ # Fetch paper info from arXiv
23
+ ##################################################
24
  def fetch_arxiv_paper(arxiv_input):
25
  """
26
+ Fetch paper title & abstract from an arXiv URL or ID.
27
  """
28
  try:
 
29
  if "arxiv.org" in arxiv_input:
30
  parsed = urlparse(arxiv_input)
31
  path = parsed.path
32
  arxiv_id = path.split("/")[-1].replace(".pdf", "")
33
  else:
 
34
  arxiv_id = arxiv_input.strip()
35
 
 
36
  api_url = f"http://export.arxiv.org/api/query?id_list={arxiv_id}"
37
  resp = requests.get(api_url)
38
  if resp.status_code != 200:
 
40
  "title": "",
41
  "abstract": "",
42
  "success": False,
43
+ "message": "Error fetching paper from arXiv API",
44
  }
45
 
 
46
  root = ET.fromstring(resp.text)
47
  ns = {"arxiv": "http://www.w3.org/2005/Atom"}
48
  entry = root.find(".//arxiv:entry", ns)
49
  if entry is None:
50
+ return {"title": "", "abstract": "", "success": False, "message": "Paper not found"}
 
 
 
 
 
51
 
52
  title = entry.find("arxiv:title", ns).text.strip()
53
  abstract = entry.find("arxiv:summary", ns).text.strip()
54
+
55
  return {
56
  "title": title,
57
  "abstract": abstract,
58
  "success": True,
59
+ "message": "Paper fetched successfully!",
60
  }
61
  except Exception as e:
62
  return {
63
  "title": "",
64
  "abstract": "",
65
  "success": False,
66
+ "message": f"Error fetching paper: {e}",
67
  }
68
 
69
+ ##################################################
70
+ # Prediction function
71
+ ##################################################
72
  @spaces.GPU(duration=60, enable_queue=True)
73
  def predict(title, abstract):
74
  """
75
+ Predict a normalized academic impact score (0–1) from title & abstract.
 
76
  """
77
  global model, tokenizer
 
78
  if model is None:
79
+ # 1) Load config
80
  config = AutoConfig.from_pretrained(model_path)
81
+
82
+ # 2) Remove quantization_config if it exists to avoid `NoneType` error in PEFT
83
+ # This ensures that 'quantization_config.to_dict()' won't be called
84
+ if hasattr(config, "quantization_config"):
85
+ del config.quantization_config
86
+
87
+ # 3) (Optional) We can still set config.num_labels = 1 if needed
88
+ config.num_labels = 1
89
 
90
+ # 4) Load the model
91
+ model_loaded = AutoModelForSequenceClassification.from_pretrained(
92
  model_path,
93
+ config=config, # pass config
94
+ torch_dtype=torch.float32,
95
+ device_map=None, # manual device
96
  low_cpu_mem_usage=False
97
  )
98
+ model_loaded.to(device)
99
+ model_loaded.eval()
100
 
101
+ # 5) Load tokenizer
102
+ tokenizer_loaded = AutoTokenizer.from_pretrained(model_path)
103
 
104
+ # Assign to globals
105
+ model, tokenizer = model_loaded, tokenizer_loaded
106
+
107
+ # Construct the input text prompt
108
  text = (
109
  f"Given a certain paper,\n"
110
  f"Title: {title.strip()}\n"
 
116
  inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=1024)
117
  inputs = {k: v.to(device) for k, v in inputs.items()}
118
  with torch.no_grad():
119
+ outputs = model(**inputs)
120
+ logits = outputs.logits
121
  prob = torch.sigmoid(logits).item()
122
+ score = min(1.0, prob + 0.05)
123
  return round(score, 4)
124
  except Exception as e:
125
+ print("Prediction error:", e)
126
+ return 0.0
127
 
128
+ ##################################################
129
+ # Grading
130
+ ##################################################
131
  def get_grade_and_emoji(score):
132
+ """Map a 0–1 score to an A/B/C style grade with an emoji."""
133
  if score >= 0.900: return "AAA 🌟"
134
  if score >= 0.800: return "AA ⭐"
135
  if score >= 0.650: return "A ✨"
 
140
  if score >= 0.300: return "CC ✏️"
141
  return "C 📑"
142
 
143
+ ##################################################
144
+ # Validation
145
+ ##################################################
146
  def validate_input(title, abstract):
147
  """
148
+ Ensure the title has at least 3 words, the abstract at least 50,
149
+ and check for ASCII-only characters.
150
  """
151
  non_ascii = re.compile(r"[^\x00-\x7F]")
152
  if len(title.split()) < 3:
 
160
  return True, "Inputs look good."
161
 
162
  def update_button_status(title, abstract):
163
+ """Enable or disable the predict button based on validation."""
164
  valid, msg = validate_input(title, abstract)
165
  if not valid:
166
  return gr.update(value="Error: " + msg), gr.update(interactive=False)
167
  return gr.update(value=msg), gr.update(interactive=True)
168
 
169
+ ##################################################
170
+ # Process arXiv input
171
+ ##################################################
172
  def process_arxiv_input(arxiv_input):
173
  """
174
+ Called when user clicks 'Fetch Paper Details' to fill in title/abstract from arXiv.
175
  """
176
  if not arxiv_input.strip():
177
  return "", "", "Please enter an arXiv URL or ID"
178
+ res = fetch_arxiv_paper(arxiv_input)
179
+ if res["success"]:
180
+ return res["title"], res["abstract"], res["message"]
181
+ return "", "", res["message"]
182
 
183
+ ##################################################
184
+ # Custom CSS
185
+ ##################################################
186
  css = """
187
  .gradio-container { font-family: Arial, sans-serif; }
188
  .main-title {
189
+ text-align: center; color: #2563eb; font-size: 2.5rem!important;
190
+ margin-bottom:1rem!important;
191
+ background: linear-gradient(45deg,#2563eb,#1d4ed8);
192
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
 
 
 
 
 
 
 
 
 
193
  }
194
  .input-section {
195
+ background:#fff; padding:1.5rem; border-radius:0.5rem;
196
+ box-shadow:0 4px 6px rgba(0,0,0,0.1);
 
 
197
  }
198
  .result-section {
199
+ background:#f7f9fc; padding:1.5rem; border-radius:0.5rem;
200
+ margin-top:2rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  }
202
  .grade-display {
203
+ font-size:2.5rem; text-align:center; margin-top:1rem;
 
 
204
  }
205
  .arxiv-input {
206
+ margin-bottom:1.5rem; padding:1rem; background:#f3f4f6;
207
+ border-radius:0.5rem;
 
 
208
  }
209
  .arxiv-link {
210
+ color:#2563eb; text-decoration: underline;
 
 
 
 
 
 
 
 
 
211
  }
212
  """
213
 
214
+ ##################################################
215
+ # Example Papers
216
+ ##################################################
217
  example_papers = [
218
  {
219
  "title": "Attention Is All You Need",
 
227
  "parallelizable and requiring significantly less time to train."
228
  ),
229
  "score": 0.982,
230
+ "note": "Revolutionary paper that introduced the Transformer architecture."
231
  },
232
  {
233
  "title": "Language Models are Few-Shot Learners",
 
237
  "typically task-agnostic in architecture, this method still requires task-specific "
238
  "fine-tuning datasets of thousands or tens of thousands of examples. By contrast, humans "
239
  "can generally perform a new language task from only a few examples or from simple "
240
+ "instructionssomething which current NLP systems still largely struggle to do. Here we "
241
  "show that scaling up language models greatly improves task-agnostic, few-shot "
242
  "performance, sometimes even reaching competitiveness with prior state-of-the-art "
243
  "fine-tuning approaches."
244
  ),
245
  "score": 0.956,
246
+ "note": "Groundbreaking GPT-3 paper on few-shot learning."
247
  },
248
  {
249
  "title": "An Empirical Study of Neural Network Training Protocols",
 
255
  "insights for deep learning practitioners."
256
  ),
257
  "score": 0.623,
258
+ "note": "Solid empirical comparison of training protocols."
259
  }
260
  ]
261
 
262
+ ##################################################
263
+ # Build the Gradio Interface
264
+ ##################################################
265
  with gr.Blocks(theme=gr.themes.Default(), css=css) as iface:
266
+ gr.Markdown("<div class='main-title'>Papers Impact: AI-Powered Research Impact Predictor</div>")
267
+ gr.Markdown("**Predict the potential research impact (0–1) from title & abstract.**")
 
 
 
 
 
 
 
268
 
269
+ # Row with input column + output column
270
  with gr.Row():
271
  with gr.Column(elem_classes="input-section"):
272
+ gr.Markdown("### Import from arXiv")
273
  with gr.Group(elem_classes="arxiv-input"):
 
274
  arxiv_input = gr.Textbox(
275
  lines=1,
276
+ placeholder="e.g. 2504.11651",
277
+ label="arXiv URL or ID",
278
  value="2504.11651"
279
  )
280
+ fetch_btn = gr.Button("🔍 Fetch Paper Details", variant="secondary")
 
 
 
 
 
 
 
 
281
 
282
+ gr.Markdown("### Or Enter Manually")
283
  title_input = gr.Textbox(
284
  lines=2,
285
+ placeholder="Paper title (3 words)...",
286
  label="Paper Title"
287
  )
288
+ abs_input = gr.Textbox(
289
  lines=5,
290
+ placeholder="Paper abstract (50 words)...",
291
  label="Paper Abstract"
292
  )
293
+ status_box = gr.Textbox(label="Validation Status", interactive=False)
294
+ predict_btn = gr.Button("🎯 Predict Impact", interactive=False, variant="primary")
295
 
296
  with gr.Column(elem_classes="result-section"):
297
+ score_box = gr.Number(label="Impact Score")
298
+ grade_box = gr.Textbox(label="Grade", elem_classes="grade-display")
 
299
 
300
+ # Validation triggers
301
+ title_input.change(update_button_status, [title_input, abs_input], [status_box, predict_btn])
302
+ abs_input.change(update_button_status, [title_input, abs_input], [status_box, predict_btn])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
 
304
+ # arXiv fetch
305
+ fetch_btn.click(process_arxiv_input, [arxiv_input], [title_input, abs_input, status_box])
 
 
 
 
 
 
 
 
 
306
 
307
+ # Predict handler
308
+ def run_predict(t, a):
309
+ s = predict(t, a)
310
+ return s, get_grade_and_emoji(s)
 
 
311
 
312
+ predict_btn.click(run_predict, [title_input, abs_input], [score_box, grade_box])
 
 
 
 
313
 
314
+ # Example papers
315
+ gr.Markdown("### Example Papers")
316
+ for paper in example_papers:
317
+ gr.Markdown(
318
+ f"**{paper['title']}** \n"
319
+ f"Score: {paper['score']} | Grade: {get_grade_and_emoji(paper['score'])} \n"
320
+ f"{paper['abstract']} \n"
321
+ f"*{paper['note']}*\n---"
322
+ )
323
 
324
+ ##################################################
325
+ # Launch
326
+ ##################################################
327
  if __name__ == "__main__":
328
  iface.launch()