openfree commited on
Commit
fd896e6
Β·
verified Β·
1 Parent(s): 8cbcecb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +104 -132
app.py CHANGED
@@ -2,22 +2,21 @@ import gradio as gr
2
  import spaces
3
  import torch
4
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
5
- import torch.nn.functional as F
6
- import torch.nn as nn
7
  import re
8
  import requests
9
  from urllib.parse import urlparse
10
  import xml.etree.ElementTree as ET
11
 
 
12
  model_path = r'ssocean/NAIP'
13
  device = 'cuda' if torch.cuda.is_available() else 'cpu'
14
 
15
- global model, tokenizer
16
  model = None
17
  tokenizer = None
18
 
19
  def fetch_arxiv_paper(arxiv_input):
20
- """Fetch paper details from arXiv URL or ID using requests."""
21
  try:
22
  if 'arxiv.org' in arxiv_input:
23
  parsed = urlparse(arxiv_input)
@@ -25,67 +24,57 @@ def fetch_arxiv_paper(arxiv_input):
25
  else:
26
  arxiv_id = arxiv_input.strip()
27
  api_url = f'http://export.arxiv.org/api/query?id_list={arxiv_id}'
28
- response = requests.get(api_url)
29
- if response.status_code != 200:
30
- return {"title": "", "abstract": "", "success": False, "message": "Error fetching paper from arXiv API"}
31
- root = ET.fromstring(response.text)
32
- ns = {'arxiv': 'http://www.w3.org/2005/Atom'}
33
- entry = root.find('.//arxiv:entry', ns)
34
  if entry is None:
35
- return {"title": "", "abstract": "", "success": False, "message": "Paper not found"}
36
- title = entry.find('arxiv:title', ns).text.strip()
37
- abstract = entry.find('arxiv:summary', ns).text.strip()
38
- return {"title": title, "abstract": abstract, "success": True, "message": "Paper fetched successfully!"}
39
  except Exception as e:
40
- return {"title": "", "abstract": "", "success": False, "message": f"Error fetching paper: {e}"}
41
 
42
  @spaces.GPU(duration=60, enable_queue=True)
43
  def predict(title, abstract):
44
- title = title.replace("\n", " ").strip().replace("''", "'")
45
- abstract = abstract.replace("\n", " ").strip().replace("''", "'")
46
  global model, tokenizer
 
 
47
  if model is None:
48
- # 1) μ „λΆ€ float32 λ‘œλ“œ
49
- try:
50
- model = AutoModelForSequenceClassification.from_pretrained(
51
- model_path,
52
- num_labels=1,
53
- device_map=None,
54
- torch_dtype=torch.float32,
55
- load_in_8bit=False,
56
- load_in_4bit=False,
57
- low_cpu_mem_usage=False
58
- )
59
- except Exception as e:
60
- print(f"첫 λ‘œλ”© μ‹€νŒ¨, μž¬μ‹œλ„: {e}")
61
- model = AutoModelForSequenceClassification.from_pretrained(
62
- model_path,
63
- num_labels=1,
64
- torch_dtype=torch.float32
65
- )
66
- # 2) device에 올렀보기 (unsupported error λ¬΄μ‹œ)
67
- try:
68
- model.to(device)
69
- except ValueError as e:
70
- print(f"model.to() λ¬΄μ‹œ: {e}")
71
  tokenizer = AutoTokenizer.from_pretrained(model_path)
 
72
  model.eval()
73
 
 
74
  text = (
75
- f"Given a certain paper, Title: {title}\n"
76
- f"Abstract: {abstract}.\n"
77
- "Predict its normalized academic impact (between 0 and 1):"
 
78
  )
 
79
  try:
80
- inputs = tokenizer(text, return_tensors="pt")
81
  inputs = {k: v.to(device) for k, v in inputs.items()}
82
  with torch.no_grad():
83
  outputs = model(**inputs)
84
  prob = torch.sigmoid(outputs.logits).item()
85
- score = min(1.0, prob + 0.05)
86
  return round(score, 4)
87
  except Exception as e:
88
- print(f"Prediction error: {e}")
89
  return 0.0
90
 
91
  def get_grade_and_emoji(score):
@@ -99,40 +88,18 @@ def get_grade_and_emoji(score):
99
  if score >= 0.300: return "CC ✏️"
100
  return "C πŸ“‘"
101
 
102
- example_papers = [
103
- {
104
- "title": "Attention Is All You Need",
105
- "abstract": "The dominant sequence transduction models are based on complex recurrent or convolutional neural networks that include an encoder and a decoder. The best performing models also connect the encoder and decoder through an attention mechanism. We propose a new simple network architecture, the Transformer, based solely on attention mechanisms, dispensing with recurrence and convolutions entirely. Experiments on two machine translation tasks show these models to be superior in quality while being more parallelizable and requiring significantly less time to train.",
106
- "score": 0.982,
107
- "note": "πŸ’« Revolutionary paper that introduced the Transformer architecture, fundamentally changing NLP and deep learning."
108
- },
109
- {
110
- "title": "Language Models are Few-Shot Learners",
111
- "abstract": "Recent work has demonstrated substantial gains on many NLP tasks and benchmarks by pre-training on a large corpus of text followed by fine-tuning on a specific task. While typically task-agnostic in architecture, this method still requires task-specific fine-tuning datasets of thousands or tens of thousands of examples. By contrast, humans can generally perform a new language task from only a few examples or from simple instructions - something which current NLP systems still largely struggle to do. Here we show that scaling up language models greatly improves task-agnostic, few-shot performance, sometimes even reaching competitiveness with prior state-of-the-art fine-tuning approaches.",
112
- "score": 0.956,
113
- "note": "πŸš€ Groundbreaking GPT-3 paper that demonstrated the power of large language models."
114
- },
115
- {
116
- "title": "An Empirical Study of Neural Network Training Protocols",
117
- "abstract": "This paper presents a comparative analysis of different training protocols for neural networks across various architectures. We examine the effects of learning rate schedules, batch size selection, and optimization algorithms on model convergence and final performance. Our experiments span multiple datasets and model sizes, providing practical insights for deep learning practitioners.",
118
- "score": 0.623,
119
- "note": "πŸ“š Solid research paper with useful findings but more limited scope and impact."
120
- }
121
- ]
122
-
123
  def validate_input(title, abstract):
124
- title = title.replace("\n", " ").strip().replace("''", "'")
125
- abstract = abstract.replace("\n", " ").strip().replace("''", "'")
126
  non_latin = re.compile(r'[^\u0000-\u007F]')
127
  if len(title.split()) < 3:
128
- return False, "The title must be at least 3 words long."
129
  if len(abstract.split()) < 50:
130
- return False, "The abstract must be at least 50 words long."
131
  if non_latin.search(title):
132
- return False, "Title에 μ˜μ–΄ μ™Έ λ¬Έμžκ°€ ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€."
133
  if non_latin.search(abstract):
134
- return False, "Abstract에 μ˜μ–΄ μ™Έ λ¬Έμžκ°€ ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€."
135
- return True, "Inputs are valid!"
136
 
137
  def update_button_status(title, abstract):
138
  valid, msg = validate_input(title, abstract)
@@ -142,12 +109,13 @@ def update_button_status(title, abstract):
142
 
143
  def process_arxiv_input(arxiv_input):
144
  if not arxiv_input.strip():
145
- return "", "", "Please enter an arXiv URL or ID"
146
- result = fetch_arxiv_paper(arxiv_input)
147
- if result["success"]:
148
- return result["title"], result["abstract"], result["message"]
149
- return "", "", result["message"]
150
 
 
151
  css = """
152
  .gradio-container {
153
  font-family: 'Arial', sans-serif;
@@ -216,53 +184,41 @@ css = """
216
  }
217
  """
218
 
 
219
  with gr.Blocks(theme=gr.themes.Default(), css=css) as iface:
220
  gr.Markdown(
221
  """
222
- # Papers Impact: AI-Powered Research Impact Predictor
223
  ## https://discord.gg/openfreeai
224
- """
225
- )
226
  gr.HTML("""<a href="https://visitorbadge.io/status?path=https%3A%2F%2FVIDraft-PaperImpact.hf.space">
227
  <img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2FVIDraft-PaperImpact.hf.space&countColor=%23263759" />
228
  </a>""")
229
 
230
  with gr.Row():
231
  with gr.Column(elem_classes="input-section"):
232
- with gr.Group(elem_classes="arxiv-input"):
233
- gr.Markdown("### πŸ“‘ Import from arXiv")
234
- arxiv_input = gr.Textbox(
235
- lines=1,
236
- placeholder="Enter arXiv URL or ID (e.g., 2501.09751)",
237
- label="arXiv Paper URL/ID",
238
- value="https://arxiv.org/pdf/2502.07316"
239
- )
240
- gr.Markdown("""
241
- <p class="arxiv-note">
242
- Click input field to use example paper or browse papers at
243
- <a href="https://arxiv.org" target="_blank" class="arxiv-link">arxiv.org</a>
244
- </p>
245
- """)
246
- fetch_button = gr.Button("πŸ” Fetch Paper Details", variant="secondary")
247
-
248
- gr.Markdown("### πŸ“ Or Enter Paper Details Manually")
249
  title_input = gr.Textbox(
250
  lines=2,
251
- placeholder="Enter Paper Title (minimum 3 words)...",
252
- label="Paper Title"
253
- )
254
  abstract_input = gr.Textbox(
255
  lines=5,
256
- placeholder="Enter Paper Abstract (minimum 50 words)...",
257
- label="Paper Abstract"
258
- )
259
- validation_status = gr.Textbox(label="βœ”οΈ Validation Status", interactive=False)
260
- submit_button = gr.Button("🎯 Predict Impact", interactive=False, variant="primary")
261
-
262
  with gr.Column(elem_classes="result-section"):
263
- with gr.Group():
264
- score_output = gr.Number(label="🎯 Impact Score")
265
- grade_output = gr.Textbox(label="πŸ† Grade", elem_classes="grade-display")
266
 
267
  with gr.Row(elem_classes="methodology-section"):
268
  gr.Markdown(
@@ -283,44 +239,60 @@ with gr.Blocks(theme=gr.themes.Default(), css=css) as iface:
283
  | Grade | Score Range | Description | Indicator |
284
  |-------|-------------|-------------|-----------|
285
  | AAA | 0.900-1.000 | Exceptional Impact | 🌟 |
286
- | AA | 0.800-0.899 | Very High Impact | ⭐ |
287
- | A | 0.650-0.799 | High Impact | ✨ |
288
- | BBB | 0.600-0.649 | Above Average Impact | πŸ”΅ |
289
- | BB | 0.550-0.599 | Moderate Impact | πŸ“˜ |
290
- | B | 0.500-0.549 | Average Impact | πŸ“– |
291
- | CCC | 0.400-0.499 | Below Average Impact | πŸ“ |
292
- | CC | 0.300-0.399 | Low Impact | ✏️ |
293
- | C | < 0.299 | Limited Impact | πŸ“‘ |
294
  """
295
  )
296
 
297
  with gr.Row(elem_classes="example-section"):
298
  gr.Markdown("### πŸ“‹ Example Papers")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  for paper in example_papers:
300
  gr.Markdown(
301
  f"""
302
  #### {paper['title']}
303
- **Score**: {paper.get('score', 'N/A')} | **Grade**: {get_grade_and_emoji(paper.get('score', 0))}
304
  {paper['abstract']}
305
  *{paper['note']}*
306
  ---
307
  """
308
  )
309
 
310
- title_input.change(update_button_status, [title_input, abstract_input], [validation_status, submit_button])
311
- abstract_input.change(update_button_status, [title_input, abstract_input], [validation_status, submit_button])
312
- fetch_button.click(process_arxiv_input, [arxiv_input], [title_input, abstract_input, validation_status])
 
313
 
314
- def process_prediction(title, abstract):
315
- score = predict(title, abstract)
316
- grade = get_grade_and_emoji(score)
317
- return score, grade
318
 
319
- submit_button.click(
320
- process_prediction,
321
- inputs=[title_input, abstract_input],
322
- outputs=[score_output, grade_output]
323
- )
324
 
325
  if __name__ == "__main__":
326
  iface.launch()
 
2
  import spaces
3
  import torch
4
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
 
 
5
  import re
6
  import requests
7
  from urllib.parse import urlparse
8
  import xml.etree.ElementTree as ET
9
 
10
+ # λͺ¨λΈ κ²½λ‘œμ™€ λ””λ°”μ΄μŠ€ μ„€μ •
11
  model_path = r'ssocean/NAIP'
12
  device = 'cuda' if torch.cuda.is_available() else 'cpu'
13
 
14
+ # μ „μ—­ λ³€μˆ˜λ‘œ λͺ¨λΈΒ·ν† ν¬λ‚˜μ΄μ € μ„ μ–Έ
15
  model = None
16
  tokenizer = None
17
 
18
  def fetch_arxiv_paper(arxiv_input):
19
+ """arXiv URL λ˜λŠ” IDλ‘œλΆ€ν„° 제λͺ©κ³Ό μš”μ•½(fetch)"""
20
  try:
21
  if 'arxiv.org' in arxiv_input:
22
  parsed = urlparse(arxiv_input)
 
24
  else:
25
  arxiv_id = arxiv_input.strip()
26
  api_url = f'http://export.arxiv.org/api/query?id_list={arxiv_id}'
27
+ resp = requests.get(api_url)
28
+ if resp.status_code != 200:
29
+ return {"title":"", "abstract":"", "success":False, "message":"arXiv API μ—λŸ¬"}
30
+ root = ET.fromstring(resp.text)
31
+ ns = {'atom': 'http://www.w3.org/2005/Atom'}
32
+ entry = root.find('.//atom:entry', ns)
33
  if entry is None:
34
+ return {"title":"", "abstract":"", "success":False, "message":"논문을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€"}
35
+ title = entry.find('atom:title', ns).text.strip()
36
+ abstract = entry.find('atom:summary', ns).text.strip()
37
+ return {"title": title, "abstract": abstract, "success": True, "message": "μ„±κ³΅μ μœΌλ‘œ κ°€μ Έμ™”μŠ΅λ‹ˆλ‹€!"}
38
  except Exception as e:
39
+ return {"title":"", "abstract":"", "success":False, "message":f"였λ₯˜: {e}"}
40
 
41
  @spaces.GPU(duration=60, enable_queue=True)
42
  def predict(title, abstract):
43
+ """λ…Όλ¬Έ 제λͺ©κ³Ό μš”μ•½μ„ λ°›μ•„ 0~1 μ‚¬μ΄μ˜ impact score 예츑"""
 
44
  global model, tokenizer
45
+
46
+ # 졜초 호좜 μ‹œ λͺ¨λΈΒ·ν† ν¬λ‚˜μ΄μ € λ‘œλ“œ
47
  if model is None:
48
+ model = AutoModelForSequenceClassification.from_pretrained(
49
+ model_path,
50
+ num_labels=1,
51
+ quantization_config=None, # bitsandbytes μ–‘μžν™” μ™„μ „ λΉ„ν™œμ„±ν™”
52
+ torch_dtype=torch.float32, # μ „λΆ€ float32
53
+ device_map=None, # accelerate dispatch λΉ„ν™œμ„±
54
+ low_cpu_mem_usage=False
55
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  tokenizer = AutoTokenizer.from_pretrained(model_path)
57
+ model.to(device)
58
  model.eval()
59
 
60
+ # μž…λ ₯ ν…μŠ€νŠΈ ꡬ성
61
  text = (
62
+ f"Given a certain paper,\n"
63
+ f"Title: {title.strip()}\n"
64
+ f"Abstract: {abstract.strip()}\n"
65
+ f"Predict its normalized academic impact (0~1):"
66
  )
67
+
68
  try:
69
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=1024)
70
  inputs = {k: v.to(device) for k, v in inputs.items()}
71
  with torch.no_grad():
72
  outputs = model(**inputs)
73
  prob = torch.sigmoid(outputs.logits).item()
74
+ score = min(1.0, prob + 0.05) # +0.05 보정, μ΅œλŒ€ 1.0
75
  return round(score, 4)
76
  except Exception as e:
77
+ print("Prediction error:", e)
78
  return 0.0
79
 
80
  def get_grade_and_emoji(score):
 
88
  if score >= 0.300: return "CC ✏️"
89
  return "C πŸ“‘"
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  def validate_input(title, abstract):
92
+ """제λͺ©Β·μš”μ•½ κΈ€μž 수 및 λΉ„μ˜μ–΄ 문자 검사"""
 
93
  non_latin = re.compile(r'[^\u0000-\u007F]')
94
  if len(title.split()) < 3:
95
+ return False, "제λͺ©μ€ μ΅œμ†Œ 3단어 이상이어야 ν•©λ‹ˆλ‹€."
96
  if len(abstract.split()) < 50:
97
+ return False, "μš”μ•½μ€ μ΅œμ†Œ 50단어 이상이어야 ν•©λ‹ˆλ‹€."
98
  if non_latin.search(title):
99
+ return False, "제λͺ©μ— μ˜μ–΄ μ™Έ λ¬Έμžκ°€ ν¬ν•¨λ˜μ—ˆμŠ΅λ‹ˆλ‹€."
100
  if non_latin.search(abstract):
101
+ return False, "μš”μ•½μ— μ˜μ–΄ μ™Έ λ¬Έμžκ°€ ν¬ν•¨λ˜μ—ˆμŠ΅λ‹ˆλ‹€."
102
+ return True, "μž…λ ₯ μœ νš¨ν•©λ‹ˆλ‹€."
103
 
104
  def update_button_status(title, abstract):
105
  valid, msg = validate_input(title, abstract)
 
109
 
110
  def process_arxiv_input(arxiv_input):
111
  if not arxiv_input.strip():
112
+ return "", "", "URL λ˜λŠ” IDλ₯Ό μž…λ ₯ν•˜μ„Έμš”"
113
+ res = fetch_arxiv_paper(arxiv_input)
114
+ if res["success"]:
115
+ return res["title"], res["abstract"], res["message"]
116
+ return "", "", res["message"]
117
 
118
+ # CSS μ •μ˜
119
  css = """
120
  .gradio-container {
121
  font-family: 'Arial', sans-serif;
 
184
  }
185
  """
186
 
187
+ # Gradio UI ꡬ성
188
  with gr.Blocks(theme=gr.themes.Default(), css=css) as iface:
189
  gr.Markdown(
190
  """
191
+ # Papers Impact: AI-Powered Research Impact Predictor
192
  ## https://discord.gg/openfreeai
193
+ """)
 
194
  gr.HTML("""<a href="https://visitorbadge.io/status?path=https%3A%2F%2FVIDraft-PaperImpact.hf.space">
195
  <img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2FVIDraft-PaperImpact.hf.space&countColor=%23263759" />
196
  </a>""")
197
 
198
  with gr.Row():
199
  with gr.Column(elem_classes="input-section"):
200
+ gr.Markdown("### πŸ“‘ arXivμ—μ„œ 뢈러였기")
201
+ arxiv_input = gr.Textbox(
202
+ lines=1,
203
+ placeholder="arXiv URL λ˜λŠ” ID",
204
+ label="arXiv URL/ID")
205
+ fetch_btn = gr.Button("πŸ” 뢈러였기", variant="secondary")
206
+
207
+ gr.Markdown("### πŸ“ 직접 μž…λ ₯")
 
 
 
 
 
 
 
 
 
208
  title_input = gr.Textbox(
209
  lines=2,
210
+ placeholder="λ…Όλ¬Έ 제λͺ© (μ΅œμ†Œ 3단어)",
211
+ label="제λͺ©")
 
212
  abstract_input = gr.Textbox(
213
  lines=5,
214
+ placeholder="λ…Όλ¬Έ μš”μ•½ (μ΅œμ†Œ 50단어)",
215
+ label="μš”μ•½")
216
+ status = gr.Textbox(label="βœ”οΈ μž…λ ₯ μƒνƒœ", interactive=False)
217
+ submit_btn = gr.Button("🎯 μ˜ˆμΈ‘ν•˜κΈ°", interactive=False, variant="primary")
218
+
 
219
  with gr.Column(elem_classes="result-section"):
220
+ score_out = gr.Number(label="🎯 Impact Score")
221
+ grade_out = gr.Textbox(label="πŸ† Grade", elem_classes="grade-display")
 
222
 
223
  with gr.Row(elem_classes="methodology-section"):
224
  gr.Markdown(
 
239
  | Grade | Score Range | Description | Indicator |
240
  |-------|-------------|-------------|-----------|
241
  | AAA | 0.900-1.000 | Exceptional Impact | 🌟 |
242
+ | AA | 0.800-0.899 | Very High Impact | ⭐ |
243
+ | A | 0.650-0.799 | High Impact | ✨ |
244
+ | BBB | 0.600-0.649 | Above Average | πŸ”΅ |
245
+ | BB | 0.550-0.599 | Moderate Impact | πŸ“˜ |
246
+ | B | 0.500-0.549 | Average Impact | πŸ“– |
247
+ | CCC | 0.400-0.499 | Below Average | πŸ“ |
248
+ | CC | 0.300-0.399 | Low Impact | ✏️ |
249
+ | C | <0.299 | Limited Impact | πŸ“‘ |
250
  """
251
  )
252
 
253
  with gr.Row(elem_classes="example-section"):
254
  gr.Markdown("### πŸ“‹ Example Papers")
255
+ example_papers = [
256
+ {
257
+ "title": "Attention Is All You Need",
258
+ "abstract": "The dominant sequence transduction models are based on complex recurrent or convolutional neural networks that include an encoder and a decoder. The best performing models also connect the encoder and decoder through an attention mechanism. We propose a new simple network architecture, the Transformer, based solely on attention mechanisms, dispensing with recurrence and convolutions entirely. Experiments on two machine translation tasks show these models to be superior in quality while being more parallelizable and requiring significantly less time to train.",
259
+ "score": 0.982,
260
+ "note": "πŸ’« Revolutionary paper that introduced the Transformer architecture, fundamentally changing NLP and deep learning."
261
+ },
262
+ {
263
+ "title": "Language Models are Few-Shot Learners",
264
+ "abstract": "Recent work has demonstrated substantial gains on many NLP tasks and benchmarks by pre-training on a large corpus of text followed by fine-tuning on a specific task. While typically task-agnostic in architecture, this method still requires task-specific fine-tuning datasets of thousands or tens of thousands of examples. By contrast, humans can generally perform a new language task from only a few examples or from simple instructions - something which current NLP systems still largely struggle to do. Here we show that scaling up language models greatly improves task-agnostic, few-shot performance, sometimes even reaching competitiveness with prior state-of-the-art fine-tuning approaches.",
265
+ "score": 0.956,
266
+ "note": "πŸš€ Groundbreaking GPT-3 paper that demonstrated the power of large language models."
267
+ },
268
+ {
269
+ "title": "An Empirical Study of Neural Network Training Protocols",
270
+ "abstract": "This paper presents a comparative analysis of different training protocols for neural networks across various architectures. We examine the effects of learning rate schedules, batch size selection, and optimization algorithms on model convergence and final performance. Our experiments span multiple datasets and model sizes, providing practical insights for deep learning practitioners.",
271
+ "score": 0.623,
272
+ "note": "πŸ“š Solid research paper with useful findings but more limited scope and impact."
273
+ }
274
+ ]
275
  for paper in example_papers:
276
  gr.Markdown(
277
  f"""
278
  #### {paper['title']}
279
+ **Score**: {paper['score']} | **Grade**: {get_grade_and_emoji(paper['score'])}
280
  {paper['abstract']}
281
  *{paper['note']}*
282
  ---
283
  """
284
  )
285
 
286
+ # 이벀트 ν•Έλ“€λŸ¬ μ—°κ²°
287
+ title_input.change(update_button_status, [title_input, abstract_input], [status, submit_btn])
288
+ abstract_input.change(update_button_status, [title_input, abstract_input], [status, submit_btn])
289
+ fetch_btn.click(process_arxiv_input, [arxiv_input], [title_input, abstract_input, status])
290
 
291
+ def run_predict(t, a):
292
+ s = predict(t, a)
293
+ return s, get_grade_and_emoji(s)
 
294
 
295
+ submit_btn.click(run_predict, [title_input, abstract_input], [score_out, grade_out])
 
 
 
 
296
 
297
  if __name__ == "__main__":
298
  iface.launch()