VibecoderMcSwaggins commited on
Commit
ffa3ae4
·
1 Parent(s): 7dc996f

Initial deployment: Antibody non-specificity predictor

Browse files

- ESM-1v (650M) + Logistic Regression
- Trained on Boughter dataset
- Pydantic v2 validation
- Gradio 5.x UI

Files changed (1) hide show
  1. app.py +40 -102
app.py CHANGED
@@ -89,25 +89,39 @@ def predict_sequence(
89
  # Predict
90
  result = predictor.predict_single(request)
91
 
92
- # --- Generate HTML Card ---
93
  is_specific = result.prediction == "specific"
94
 
 
 
 
 
 
 
95
  if is_specific:
96
- color_class = "status-safe"
 
 
 
97
  icon = "✅"
98
  title = "Specific (Safe)"
99
  msg = "Low risk of polyreactivity"
100
  else:
101
- color_class = "status-danger"
 
 
 
102
  icon = "⚠️"
103
  title = "Non-Specific (Risk)"
104
  msg = "High risk of polyreactivity"
105
 
106
  html_card = f"""
107
- <div class="status-card {color_class}">
108
- <span class="status-icon">{icon}</span>
109
- <div class="status-text">{title}</div>
110
- <div class="status-subtext">{msg}</div>
 
 
111
  </div>
112
  """
113
 
@@ -140,82 +154,6 @@ def predict_sequence(
140
  raise gr.Error(f"Prediction failed: {str(e)}") from e
141
 
142
 
143
- # --- Custom CSS (with !important to override Gradio defaults) ---
144
- css = """
145
- .gradio-container {
146
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
147
- }
148
- .header-text {
149
- text-align: center !important;
150
- margin-bottom: 20px !important;
151
- }
152
- .header-title {
153
- font-size: 2.5rem !important;
154
- font-weight: 700 !important;
155
- background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%) !important;
156
- -webkit-background-clip: text !important;
157
- -webkit-text-fill-color: transparent !important;
158
- margin-bottom: 0.5rem !important;
159
- }
160
- .header-subtitle {
161
- font-size: 1.1rem !important;
162
- color: #6b7280 !important;
163
- }
164
- .status-card {
165
- padding: 30px !important;
166
- border-radius: 16px !important;
167
- text-align: center !important;
168
- margin-bottom: 20px !important;
169
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important;
170
- transition: all 0.3s ease !important;
171
- }
172
- .status-safe {
173
- background-color: #ecfdf5 !important;
174
- border: 2px solid #10b981 !important;
175
- color: #065f46 !important;
176
- }
177
- .status-danger {
178
- background-color: #fef2f2 !important;
179
- border: 2px solid #ef4444 !important;
180
- color: #991b1b !important;
181
- }
182
- .status-icon {
183
- font-size: 48px !important;
184
- display: block !important;
185
- margin-bottom: 15px !important;
186
- }
187
- .status-text {
188
- font-size: 28px !important;
189
- font-weight: 800 !important;
190
- letter-spacing: -0.025em !important;
191
- margin-bottom: 5px !important;
192
- }
193
- .status-subtext {
194
- font-size: 16px !important;
195
- opacity: 0.9 !important;
196
- }
197
- .footer-links {
198
- text-align: center !important;
199
- margin-top: 40px !important;
200
- padding-top: 20px !important;
201
- border-top: 1px solid #e5e7eb !important;
202
- color: #9ca3af !important;
203
- font-size: 0.9rem !important;
204
- }
205
- .footer-links a {
206
- color: #6b7280 !important;
207
- text-decoration: none !important;
208
- margin: 0 10px !important;
209
- }
210
- .footer-links a:hover {
211
- color: #3b82f6 !important;
212
- text-decoration: underline !important;
213
- }
214
- """
215
-
216
- # Inline CSS injection via HTML to survive HF Spaces iframe stripping
217
- inline_style = f"<style>{css}</style>"
218
-
219
  # --- Example Sequences ---
220
  examples = [
221
  [
@@ -237,19 +175,19 @@ examples = [
237
 
238
  # --- Gradio Blocks App ---
239
  with gr.Blocks(theme=gr.themes.Soft(), title="Antibody Predictor") as app:
240
- # Inject CSS via gr.HTML() to bypass HF Spaces iframe stripping of css parameter
241
- gr.HTML(inline_style)
242
-
243
- # Header
244
- with gr.Column(elem_classes="header-text"):
245
- gr.Markdown(
246
- """
247
- <div class="header-title">🧬 Antibody Non-Specificity Predictor</div>
248
- <div class="header-subtitle">
249
  Assess polyreactivity risk using ESM-1v Protein Language Models
250
  </div>
251
- """
252
- )
 
253
 
254
  # Main Content
255
  with gr.Row(equal_height=False):
@@ -297,10 +235,10 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Antibody Predictor") as app:
297
  result_html = gr.HTML(
298
  label="Prediction Status",
299
  value="""
300
- <div class="status-card" style="background-color: #f3f4f6; border: 2px dashed #d1d5db; color: #6b7280;">
301
- <span class="status-icon">⏳</span>
302
- <div class="status-text">Ready to Predict</div>
303
- <div class="status-subtext">Enter a sequence to begin analysis</div>
304
  </div>
305
  """,
306
  )
@@ -315,13 +253,13 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Antibody Predictor") as app:
315
  json_output = gr.JSON(label="Raw Result")
316
 
317
  # Footer
318
- gr.Markdown(
319
  """
320
- <div class="footer-links">
321
  Model: ESM-1v (650M) + Logistic Regression • Training: Boughter et al. (914 sequences)
322
  <br>
323
- <a href="https://huggingface.co/facebook/esm1v_t33_650M_UR90S_1" target="_blank">ESM-1v Model</a> •
324
- <a href="#" target="_blank">Paper Citation (Sakhnini et al. 2025)</a>
325
  </div>
326
  """
327
  )
 
89
  # Predict
90
  result = predictor.predict_single(request)
91
 
92
+ # --- Generate HTML Card (inline styles survive HF Spaces iframe stripping) ---
93
  is_specific = result.prediction == "specific"
94
 
95
+ base_style = (
96
+ "padding:30px;border-radius:16px;text-align:center;"
97
+ "margin-bottom:20px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.1);"
98
+ "transition:all 0.3s ease;"
99
+ )
100
+
101
  if is_specific:
102
+ card_style = (
103
+ base_style
104
+ + "background-color:#ecfdf5;border:2px solid #10b981;color:#065f46;"
105
+ )
106
  icon = "✅"
107
  title = "Specific (Safe)"
108
  msg = "Low risk of polyreactivity"
109
  else:
110
+ card_style = (
111
+ base_style
112
+ + "background-color:#fef2f2;border:2px solid #ef4444;color:#991b1b;"
113
+ )
114
  icon = "⚠️"
115
  title = "Non-Specific (Risk)"
116
  msg = "High risk of polyreactivity"
117
 
118
  html_card = f"""
119
+ <div style="{card_style}">
120
+ <span style="font-size:48px;display:block;margin-bottom:15px;">{icon}</span>
121
+ <div style="font-size:28px;font-weight:800;letter-spacing:-0.025em;margin-bottom:5px;">
122
+ {title}
123
+ </div>
124
+ <div style="font-size:16px;opacity:0.9;">{msg}</div>
125
  </div>
126
  """
127
 
 
154
  raise gr.Error(f"Prediction failed: {str(e)}") from e
155
 
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  # --- Example Sequences ---
158
  examples = [
159
  [
 
175
 
176
  # --- Gradio Blocks App ---
177
  with gr.Blocks(theme=gr.themes.Soft(), title="Antibody Predictor") as app:
178
+ # Header (inline styles to survive HF Spaces stripping)
179
+ gr.HTML(
180
+ """
181
+ <div style="text-align:center;margin-bottom:20px;font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;">
182
+ <div style="font-size:2.4rem;font-weight:700;color:#3b82f6;margin-bottom:8px;">
183
+ 🧬 Antibody Non-Specificity Predictor
184
+ </div>
185
+ <div style="font-size:1.1rem;color:#6b7280;">
 
186
  Assess polyreactivity risk using ESM-1v Protein Language Models
187
  </div>
188
+ </div>
189
+ """
190
+ )
191
 
192
  # Main Content
193
  with gr.Row(equal_height=False):
 
235
  result_html = gr.HTML(
236
  label="Prediction Status",
237
  value="""
238
+ <div style="padding:30px;border-radius:16px;text-align:center;margin-bottom:20px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.1);background-color:#f3f4f6;border:2px dashed #d1d5db;color:#374151;">
239
+ <span style="font-size:48px;display:block;margin-bottom:15px;">⏳</span>
240
+ <div style="font-size:28px;font-weight:800;letter-spacing:-0.025em;margin-bottom:5px;">Ready to Predict</div>
241
+ <div style="font-size:16px;opacity:0.9;">Enter a sequence to begin analysis</div>
242
  </div>
243
  """,
244
  )
 
253
  json_output = gr.JSON(label="Raw Result")
254
 
255
  # Footer
256
+ gr.HTML(
257
  """
258
+ <div style="text-align:center;margin-top:32px;padding-top:16px;border-top:1px solid #e5e7eb;color:#6b7280;font-size:0.95rem;font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;">
259
  Model: ESM-1v (650M) + Logistic Regression • Training: Boughter et al. (914 sequences)
260
  <br>
261
+ <a style="color:#6b7280;text-decoration:none;margin:0 10px;" href="https://huggingface.co/facebook/esm1v_t33_650M_UR90S_1" target="_blank">ESM-1v Model</a> •
262
+ <a style="color:#6b7280;text-decoration:none;margin:0 10px;" href="#" target="_blank">Paper Citation (Sakhnini et al. 2025)</a>
263
  </div>
264
  """
265
  )