Mardiyyah commited on
Commit
9dde9e9
·
verified ·
1 Parent(s): 4dba995

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +355 -111
app.py CHANGED
@@ -5,9 +5,13 @@ import gradio as gr
5
  from transformers import pipeline
6
 
7
 
8
- # -----------------------------
9
- # Configuration
10
- # -----------------------------
 
 
 
 
11
 
12
  RAW_MODEL_REGISTRY = {
13
  "CeLLaTe_contracted_ent_Reinit_LLRD": "OTAR3088/CeLLaTe_contracted_ent_Reinit_llrd",
@@ -38,22 +42,6 @@ RAW_MODEL_REGISTRY = {
38
  "CeLLaTe-V1-Model": "OTAR3088/bioformer-cellate_V1",
39
  }
40
 
41
- # Explicit metadata is better than inferring backend from the model name.
42
- MODEL_REGISTRY = {
43
- name: {"repo_id": repo_id, "backend": "hf", "enabled": True}
44
- for name, repo_id in RAW_MODEL_REGISTRY.items()
45
- }
46
-
47
- ENABLE_FLAIR = False
48
-
49
- AVAILABLE_MODELS = {
50
- name: cfg
51
- for name, cfg in MODEL_REGISTRY.items()
52
- if cfg["enabled"] and (ENABLE_FLAIR or cfg["backend"] != "flair")
53
- }
54
-
55
- DEFAULT_MODEL = "CeLLaTe_contracted_ent_Reinit_LLRD"
56
-
57
  EXAMPLES = [
58
  """Aside from in vivo models, numerous studies investigating bacterial virulence and pathogenesis have also
59
  employed in vitro cell line models to gain an initial understanding of the intricate host-pathogen interactions.
@@ -64,32 +52,73 @@ HeLa cells, which originate from human cervical epithelial cells, are thus frequ
64
  A. baumannii frequently infects human epithelial tissues, such as the respiratory system, skin and mucosal linings [15].
65
  HeLa cells are resilient and readily cultured in vitro, exhibiting a rapid growth rate.
66
  This ensures the availability of a uniform and consistent cell population for studies, rendering them economical and reliable.""",
67
- """Based on the limma R package, a total of 2578 (DEGs 1398 downregulated and 1188 upregulated) were screened out from GEO: GSE225819 data...""",
68
- """Given the dynamic nature of T cell activation and the heterogeneity of CD4+ T cells, we mapped gene expression regulation using single-cell transcriptomes spanning four time points of CD4+ T cell activation...""",
69
- """To quantitatively characterise the macrophage foetal-like profile observed, we projected adult and foetal macrophages data from the human decidual–placental interface into our data set...""",
70
- """Peripheral blood mononuclear cells (PBMCs) were isolated using Ficoll-Paque PLUS (GE Healthcare) density gradient centrifugation...""",
71
- """We performed dimensionality reduction and embedding using uniform manifold approximation (UMAP) (ref. 21) (Methods) and observed that cells separated by time point of stimulation...""",
72
- """Finally, we leveraged this model to experimentally evaluate genes linked to immune-related phenotypes by GWAS...""",
 
 
 
 
 
 
 
73
  ]
74
 
75
- EXAMPLE_OPTIONS = {
76
- f"Example {i + 1}": text.strip()
77
- for i, text in enumerate(EXAMPLES)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  }
79
 
80
  GR_THEME = gr.themes.Soft(
81
  primary_hue="indigo",
82
  secondary_hue="rose",
83
- neutral_hue="gray",
84
  )
85
 
86
  CSS = """
87
  .app-shell {
88
- max-width: 1250px;
89
  margin: 0 auto;
90
  }
91
  .hero {
92
- padding: 0.25rem 0 0.75rem 0;
93
  }
94
  .hero h1 {
95
  margin-bottom: 0.2rem;
@@ -104,68 +133,126 @@ CSS = """
104
  flex-wrap: wrap;
105
  margin-top: 0.75rem;
106
  }
107
- .badge {
108
  display: inline-block;
109
- padding: 0.25rem 0.7rem;
110
  border-radius: 999px;
111
- border: 1px solid #d6dcff;
 
112
  background: #eef2ff;
113
- font-size: 0.9rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  }
115
  """
116
 
117
  HEADER_HTML = """
118
  <div class="app-shell hero">
119
  <h1>🧬 OTAR3088 Biomedical NER Demo</h1>
120
- <p>Extract CeLLaTe biomedical entities from abstracts and paragraphs using trained NER models.</p>
121
  <div class="badge-row">
122
- <span class="badge">CellLine</span>
123
- <span class="badge">CellType</span>
124
- <span class="badge">Tissue</span>
125
  </div>
126
  </div>
127
  """
128
 
129
- ABOUT_MD = """
130
- This demo showcases CeLLaTe entity extraction models trained to identify biomedical entities relevant to knowledge discovery.
131
 
132
- **How to use**
133
- 1. Choose a model.
134
- 2. Paste biomedical text or load an example.
135
- 3. Run extraction.
136
- 4. Review tagged spans and the entity summary.
137
 
138
- **Note**
139
- This space is intended for research demonstration purposes. Models may be updated as development continues.
140
- """
141
 
142
- COLOR_MAP = {
143
- "CellLine": "#DBEAFE",
144
- "CELL_LINE": "#DBEAFE",
145
- "CellType": "#DCFCE7",
146
- "CELL_TYPE": "#DCFCE7",
147
- "Tissue": "#FCE7F3",
148
- "TISSUE": "#FCE7F3",
149
- "CELL_TISSUE": "#FCE7F3",
150
- }
151
 
 
 
 
 
152
 
153
- # -----------------------------
154
- # Model loading and inference
155
- # -----------------------------
156
 
157
- @lru_cache(maxsize=6)
158
- def load_model(model_name: str):
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  cfg = AVAILABLE_MODELS[model_name]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
- if cfg["backend"] != "hf":
162
- raise ValueError("Flair models are disabled in this demo.")
163
 
164
- return pipeline(
165
- "ner",
166
- model=cfg["repo_id"],
167
- aggregation_strategy="simple",
168
- )
169
 
170
 
171
  def normalize_entities(predictions):
@@ -199,6 +286,18 @@ def summarize_entities(entities):
199
  return sorted(rows, key=lambda row: (-row[1], row[0]))
200
 
201
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  def tagger(text, model_name):
203
  text = (text or "").strip()
204
  if not text:
@@ -207,87 +306,232 @@ def tagger(text, model_name):
207
  ner_pipe = load_model(model_name)
208
  predictions = ner_pipe(text)
209
  entities = normalize_entities(predictions)
 
210
 
211
- return {"text": text, "entities": entities}, summarize_entities(entities)
212
 
213
 
214
- def load_example(example_name):
215
- return EXAMPLE_OPTIONS.get(example_name, "")
216
 
217
 
218
- def clear_all():
219
- return "", {"text": "", "entities": []}, []
 
 
 
 
 
 
 
 
220
 
221
 
222
- # -----------------------------
223
- # UI
224
- # -----------------------------
225
 
226
- def gradio_ui():
227
  with gr.Blocks(theme=GR_THEME, css=CSS, title="OTAR3088 Biomedical NER Demo") as demo:
228
  gr.HTML(HEADER_HTML)
229
 
230
- with gr.Accordion("About this demo", open=False):
231
- gr.Markdown(ABOUT_MD)
232
-
233
  with gr.Row():
234
- model_choice = gr.Dropdown(
235
- choices=list(AVAILABLE_MODELS.keys()),
236
- value=DEFAULT_MODEL,
237
- label="Model",
238
- scale=4,
239
- )
240
-
241
- example_choice = gr.Dropdown(
242
- choices=list(EXAMPLE_OPTIONS.keys()),
243
- label="Load example",
244
- scale=3,
245
- )
246
-
247
- load_btn = gr.Button("Load", scale=1)
248
- clear_btn = gr.Button("Clear", scale=1)
249
- run_btn = gr.Button("Run extraction", variant="primary", scale=1)
250
-
251
- with gr.Row(equal_height=True):
252
- with gr.Column(scale=1):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  input_text = gr.Textbox(
254
  label="Input text",
255
  placeholder="Paste a biomedical abstract or paragraph here...",
256
- lines=18,
257
  )
258
 
259
- with gr.Column(scale=1):
260
  output_highlight = gr.HighlightedText(
261
  label="Tagged entities",
262
  color_map=COLOR_MAP,
263
- show_legend=True,
264
  )
 
265
  entity_summary = gr.Dataframe(
266
  headers=["Entity type", "Count", "Avg. confidence"],
267
  interactive=False,
268
- label="Entity summary",
269
  )
270
 
271
- load_btn.click(
272
  fn=load_example,
273
  inputs=example_choice,
274
- outputs=input_text,
 
 
 
 
 
 
275
  )
276
 
277
  run_btn.click(
278
  fn=tagger,
279
  inputs=[input_text, model_choice],
280
- outputs=[output_highlight, entity_summary],
281
  )
282
 
283
  clear_btn.click(
284
- fn=clear_all,
285
- outputs=[input_text, output_highlight, entity_summary],
286
  )
287
 
288
  return demo
289
 
290
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  if __name__ == "__main__":
292
- app = gradio_ui()
293
  app.launch()
 
5
  from transformers import pipeline
6
 
7
 
8
+ # =========================================================
9
+ # Config
10
+ # =========================================================
11
+
12
+ LAYOUT = "alt2" # "alt2" or "alt3"
13
+ ENABLE_FLAIR = False
14
+ DEFAULT_MODEL = "CeLLaTe_contracted_ent_Reinit_LLRD"
15
 
16
  RAW_MODEL_REGISTRY = {
17
  "CeLLaTe_contracted_ent_Reinit_LLRD": "OTAR3088/CeLLaTe_contracted_ent_Reinit_llrd",
 
42
  "CeLLaTe-V1-Model": "OTAR3088/bioformer-cellate_V1",
43
  }
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  EXAMPLES = [
46
  """Aside from in vivo models, numerous studies investigating bacterial virulence and pathogenesis have also
47
  employed in vitro cell line models to gain an initial understanding of the intricate host-pathogen interactions.
 
52
  A. baumannii frequently infects human epithelial tissues, such as the respiratory system, skin and mucosal linings [15].
53
  HeLa cells are resilient and readily cultured in vitro, exhibiting a rapid growth rate.
54
  This ensures the availability of a uniform and consistent cell population for studies, rendering them economical and reliable.""",
55
+
56
+ """Based on the limma R package, a total of 2578 (DEGs 1398 downregulated and 1188 upregulated) were screened out from GEO: GSE225819 data, including 20 normal samples and 20 GIST samples with liver metastasis (|log2FC| > 1; P < 0.05), suggesting that these DEGs may be involved in liver metastasis in GIST patients. The top 10 upregulated genes were PENK, IGF2, GPR20, CTSL, SCRG1, PNMAL1, NKX3-2, ANO1, PLAT, and BCHE.
57
+ Moreover, we evaluated IGF2 expression in the GIST cell line. By western blotting, expression levels of IGF2 in GIST882, GIST882-R, GIST-T1, and GIST-T1-R were higher than those in normal RGM-1.""",
58
+
59
+ """Given the dynamic nature of T cell activation and the heterogeneity of CD4+ T cells, we mapped gene expression regulation using single-cell transcriptomes spanning four time points of CD4+ T cell activation. We reconstructed activation trajectories for naive and memory CD4+ T cells and identified eQTL effects manifesting at different time points and across different subpopulations of cells.""",
60
+
61
+ """To quantitatively characterise the macrophage foetal-like profile observed, we projected adult and foetal macrophages data from the human decidual–placental interface into our data set. This unique tissue setting includes both adult/maternal monocyte-derived macrophages and foetal/placental YS-derived macrophages (Hofbauer cells).""",
62
+
63
+ """Peripheral blood mononuclear cells (PBMCs) were isolated using Ficoll-Paque PLUS (GE Healthcare) density gradient centrifugation. Naive (CD25− CD45RA+ CD45RO−) and memory (CD25− CD45RA− CD45RO+) CD4+ T cells were isolated from the PBMC fraction using EasySep naive CD4+ T cell isolation kits and memory CD4+ T cell enrichment kits.""",
64
+
65
+ """We performed dimensionality reduction and embedding using uniform manifold approximation (UMAP) and observed that cells separated by time point of stimulation, forming a gradual progression from resting to the most activated cell state. This progression was accompanied by changes in activation markers such as CD69 and IL2RA.""",
66
+
67
+ """Finally, we leveraged this model to experimentally evaluate genes linked to immune-related phenotypes by GWAS. Interestingly, CRISPR/Cas9-mediated KO of GWAS hits in iPSC-derived macrophages and DCs highlighted their potential role in physiological and pathological cell states of distinct cell types."""
68
  ]
69
 
70
+
71
+ def build_model_registry():
72
+ registry = {}
73
+ for model_name, repo_id in RAW_MODEL_REGISTRY.items():
74
+ registry[model_name] = {
75
+ "repo_id": repo_id,
76
+ "backend": "hf", # explicit backend metadata
77
+ "enabled": True,
78
+ "tag": "",
79
+ "description": "Description placeholder. Add a short note about what this model predicts and when to use it."
80
+ }
81
+
82
+ # Example override for the default model
83
+ registry[DEFAULT_MODEL]["tag"] = "best"
84
+ registry[DEFAULT_MODEL]["description"] = (
85
+ "Tags 2 entities with CellType and Tissue contracted into a single entity."
86
+ )
87
+
88
+ return registry
89
+
90
+
91
+ MODEL_REGISTRY = build_model_registry()
92
+
93
+ AVAILABLE_MODELS = {
94
+ name: cfg
95
+ for name, cfg in MODEL_REGISTRY.items()
96
+ if cfg["enabled"] and (ENABLE_FLAIR or cfg["backend"] != "flair")
97
+ }
98
+
99
+ COLOR_MAP = {
100
+ "CellLine": "#DBEAFE",
101
+ "CELL_LINE": "#DBEAFE",
102
+ "CellType": "#DCFCE7",
103
+ "CELL_TYPE": "#DCFCE7",
104
+ "Tissue": "#FCE7F3",
105
+ "TISSUE": "#FCE7F3",
106
+ "CELL_TISSUE": "#FCE7F3",
107
  }
108
 
109
  GR_THEME = gr.themes.Soft(
110
  primary_hue="indigo",
111
  secondary_hue="rose",
112
+ neutral_hue="gray"
113
  )
114
 
115
  CSS = """
116
  .app-shell {
117
+ max-width: 1320px;
118
  margin: 0 auto;
119
  }
120
  .hero {
121
+ padding: 0.25rem 0 0.8rem 0;
122
  }
123
  .hero h1 {
124
  margin-bottom: 0.2rem;
 
133
  flex-wrap: wrap;
134
  margin-top: 0.75rem;
135
  }
136
+ .pill {
137
  display: inline-block;
138
+ padding: 0.18rem 0.65rem;
139
  border-radius: 999px;
140
+ font-size: 0.84rem;
141
+ border: 1px solid #d7defb;
142
  background: #eef2ff;
143
+ color: #334155;
144
+ }
145
+ .pill-best {
146
+ background: #ecfeef;
147
+ border-color: #b7efc2;
148
+ color: #166534;
149
+ font-weight: 600;
150
+ }
151
+ .info-card {
152
+ border: 1px solid #e5e7eb;
153
+ border-radius: 14px;
154
+ padding: 14px 16px;
155
+ background: #ffffff;
156
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
157
+ }
158
+ .info-title {
159
+ font-size: 0.92rem;
160
+ color: #667085;
161
+ margin-bottom: 6px;
162
+ }
163
+ .model-name {
164
+ font-weight: 700;
165
+ margin-bottom: 6px;
166
+ }
167
+ .model-desc {
168
+ color: #334155;
169
+ line-height: 1.45;
170
+ margin-top: 8px;
171
+ }
172
+ .model-repo {
173
+ margin-top: 10px;
174
+ font-size: 0.82rem;
175
+ color: #64748b;
176
+ word-break: break-all;
177
+ }
178
+ .legend-card {
179
+ border: 1px solid #e5e7eb;
180
+ border-radius: 14px;
181
+ padding: 12px 14px;
182
+ background: #ffffff;
183
+ }
184
+ .section-note {
185
+ color: #64748b;
186
+ font-size: 0.92rem;
187
  }
188
  """
189
 
190
  HEADER_HTML = """
191
  <div class="app-shell hero">
192
  <h1>🧬 OTAR3088 Biomedical NER Demo</h1>
193
+ <p>Extract CeLLaTe biomedical entities from biomedical abstracts and paragraphs.</p>
194
  <div class="badge-row">
195
+ <span class="pill">CellLine</span>
196
+ <span class="pill">CellType</span>
197
+ <span class="pill">Tissue</span>
198
  </div>
199
  </div>
200
  """
201
 
 
 
202
 
203
+ # =========================================================
204
+ # Helpers
205
+ # =========================================================
 
 
206
 
207
+ def model_display_label(model_name: str) -> str:
208
+ tag = AVAILABLE_MODELS[model_name].get("tag", "").strip()
209
+ return f"{model_name} · {tag}" if tag else model_name
210
 
 
 
 
 
 
 
 
 
 
211
 
212
+ MODEL_CHOICES = [
213
+ (model_display_label(model_name), model_name)
214
+ for model_name in AVAILABLE_MODELS.keys()
215
+ ]
216
 
 
 
 
217
 
218
+ def make_example_label(text: str, idx: int, max_chars: int = 85) -> str:
219
+ one_line = " ".join(text.split())
220
+ preview = one_line[:max_chars].rstrip()
221
+ if len(one_line) > max_chars:
222
+ preview += "..."
223
+ return f"Example {idx + 1}: {preview}"
224
+
225
+
226
+ EXAMPLE_CHOICES = [
227
+ (make_example_label(text, i), text.strip())
228
+ for i, text in enumerate(EXAMPLES)
229
+ ]
230
+
231
+
232
+ def render_model_card(model_name: str) -> str:
233
  cfg = AVAILABLE_MODELS[model_name]
234
+ tag = cfg.get("tag", "").strip()
235
+
236
+ tag_html = ""
237
+ if tag:
238
+ tag_class = "pill pill-best" if tag.lower() == "best" else "pill"
239
+ tag_html = f'<span class="{tag_class}">{tag}</span>'
240
+
241
+ return f"""
242
+ <div class="info-card">
243
+ <div class="info-title">Model description</div>
244
+ <div style="display:flex; align-items:center; gap:8px; flex-wrap:wrap;">
245
+ <div class="model-name">{model_name}</div>
246
+ {tag_html}
247
+ </div>
248
+ <div class="model-desc">{cfg.get("description", "Description placeholder.")}</div>
249
+ <div class="model-repo"><strong>Repo:</strong> {cfg["repo_id"]}</div>
250
+ </div>
251
+ """
252
 
 
 
253
 
254
+ def load_example(example_text):
255
+ return example_text or ""
 
 
 
256
 
257
 
258
  def normalize_entities(predictions):
 
286
  return sorted(rows, key=lambda row: (-row[1], row[0]))
287
 
288
 
289
+ @lru_cache(maxsize=6)
290
+ def load_model(model_name: str):
291
+ cfg = AVAILABLE_MODELS[model_name]
292
+ if cfg["backend"] != "hf":
293
+ raise ValueError("Flair models are disabled in this space.")
294
+ return pipeline(
295
+ "ner",
296
+ model=cfg["repo_id"],
297
+ aggregation_strategy="simple",
298
+ )
299
+
300
+
301
  def tagger(text, model_name):
302
  text = (text or "").strip()
303
  if not text:
 
306
  ner_pipe = load_model(model_name)
307
  predictions = ner_pipe(text)
308
  entities = normalize_entities(predictions)
309
+ summary = summarize_entities(entities)
310
 
311
+ return {"text": text, "entities": entities}, summary
312
 
313
 
314
+ def clear_outputs():
315
+ return "", {"text": "", "entities": []}, []
316
 
317
 
318
+ def model_table_rows():
319
+ rows = []
320
+ for name, cfg in AVAILABLE_MODELS.items():
321
+ rows.append([
322
+ name,
323
+ cfg.get("tag", ""),
324
+ cfg.get("description", ""),
325
+ cfg["repo_id"]
326
+ ])
327
+ return rows
328
 
329
 
330
+ # =========================================================
331
+ # Alternative 2: Sidebar + analysis canvas
332
+ # =========================================================
333
 
334
+ def build_ui_alt2():
335
  with gr.Blocks(theme=GR_THEME, css=CSS, title="OTAR3088 Biomedical NER Demo") as demo:
336
  gr.HTML(HEADER_HTML)
337
 
 
 
 
338
  with gr.Row():
339
+ with gr.Column(scale=3, min_width=320):
340
+ model_choice = gr.Dropdown(
341
+ choices=MODEL_CHOICES,
342
+ value=DEFAULT_MODEL,
343
+ label="Model",
344
+ info="Choose the NER model to run"
345
+ )
346
+
347
+ model_info = gr.HTML(
348
+ value=render_model_card(DEFAULT_MODEL)
349
+ )
350
+
351
+ example_choice = gr.Dropdown(
352
+ choices=EXAMPLE_CHOICES,
353
+ value=None,
354
+ label="Example text",
355
+ info="Select a sample to populate the input box"
356
+ )
357
+
358
+ with gr.Row():
359
+ run_btn = gr.Button("Run extraction", variant="primary")
360
+ clear_btn = gr.Button("Clear")
361
+
362
+ gr.HTML("""
363
+ <div class="legend-card">
364
+ <div class="info-title">Supported entities</div>
365
+ <div style="display:flex; gap:8px; flex-wrap:wrap; margin-top:6px;">
366
+ <span class="pill">CellLine</span>
367
+ <span class="pill">CellType</span>
368
+ <span class="pill">Tissue</span>
369
+ </div>
370
+ <div class="section-note" style="margin-top:10px;">
371
+ Paste or load biomedical text, then run inference to inspect highlighted entities and summary counts.
372
+ </div>
373
+ </div>
374
+ """)
375
+
376
+ with gr.Column(scale=8):
377
  input_text = gr.Textbox(
378
  label="Input text",
379
  placeholder="Paste a biomedical abstract or paragraph here...",
380
+ lines=16
381
  )
382
 
 
383
  output_highlight = gr.HighlightedText(
384
  label="Tagged entities",
385
  color_map=COLOR_MAP,
386
+ show_legend=True
387
  )
388
+
389
  entity_summary = gr.Dataframe(
390
  headers=["Entity type", "Count", "Avg. confidence"],
391
  interactive=False,
392
+ label="Entity summary"
393
  )
394
 
395
+ example_choice.change(
396
  fn=load_example,
397
  inputs=example_choice,
398
+ outputs=input_text
399
+ )
400
+
401
+ model_choice.change(
402
+ fn=render_model_card,
403
+ inputs=model_choice,
404
+ outputs=model_info
405
  )
406
 
407
  run_btn.click(
408
  fn=tagger,
409
  inputs=[input_text, model_choice],
410
+ outputs=[output_highlight, entity_summary]
411
  )
412
 
413
  clear_btn.click(
414
+ fn=clear_outputs,
415
+ outputs=[input_text, output_highlight, entity_summary]
416
  )
417
 
418
  return demo
419
 
420
 
421
+ # =========================================================
422
+ # Alternative 3: Tabbed interface
423
+ # =========================================================
424
+
425
+ def build_ui_alt3():
426
+ with gr.Blocks(theme=GR_THEME, css=CSS, title="OTAR3088 Biomedical NER Demo") as demo:
427
+ gr.HTML(HEADER_HTML)
428
+
429
+ with gr.Tabs():
430
+ with gr.Tab("Demo"):
431
+ with gr.Row():
432
+ with gr.Column(scale=4, min_width=320):
433
+ model_choice = gr.Dropdown(
434
+ choices=MODEL_CHOICES,
435
+ value=DEFAULT_MODEL,
436
+ label="Model",
437
+ info="Choose the NER model to run"
438
+ )
439
+
440
+ model_info = gr.HTML(
441
+ value=render_model_card(DEFAULT_MODEL)
442
+ )
443
+
444
+ example_choice = gr.Dropdown(
445
+ choices=EXAMPLE_CHOICES,
446
+ value=None,
447
+ label="Example text",
448
+ info="Select a sample to populate the input box"
449
+ )
450
+
451
+ with gr.Row():
452
+ run_btn = gr.Button("Run extraction", variant="primary")
453
+ clear_btn = gr.Button("Clear")
454
+
455
+ with gr.Column(scale=8):
456
+ input_text = gr.Textbox(
457
+ label="Input text",
458
+ placeholder="Paste a biomedical abstract or paragraph here...",
459
+ lines=16
460
+ )
461
+
462
+ output_highlight = gr.HighlightedText(
463
+ label="Tagged entities",
464
+ color_map=COLOR_MAP,
465
+ show_legend=True
466
+ )
467
+
468
+ with gr.Accordion("Entity summary", open=True):
469
+ entity_summary = gr.Dataframe(
470
+ headers=["Entity type", "Count", "Avg. confidence"],
471
+ interactive=False,
472
+ label="Summary"
473
+ )
474
+
475
+ example_choice.change(
476
+ fn=load_example,
477
+ inputs=example_choice,
478
+ outputs=input_text
479
+ )
480
+
481
+ model_choice.change(
482
+ fn=render_model_card,
483
+ inputs=model_choice,
484
+ outputs=model_info
485
+ )
486
+
487
+ run_btn.click(
488
+ fn=tagger,
489
+ inputs=[input_text, model_choice],
490
+ outputs=[output_highlight, entity_summary]
491
+ )
492
+
493
+ clear_btn.click(
494
+ fn=clear_outputs,
495
+ outputs=[input_text, output_highlight, entity_summary]
496
+ )
497
+
498
+ with gr.Tab("Models"):
499
+ gr.Markdown("### Available models")
500
+ gr.Dataframe(
501
+ value=model_table_rows(),
502
+ headers=["Model", "Tag", "Description", "Repo"],
503
+ interactive=False,
504
+ label="Model catalogue"
505
+ )
506
+
507
+ with gr.Tab("About"):
508
+ gr.Markdown("""
509
+ ### About this demo
510
+
511
+ This space extracts CeLLaTe biomedical entities from biomedical text.
512
+
513
+ **Supported entities**
514
+ - CellLine
515
+ - CellType
516
+ - Tissue
517
+
518
+ **How to use**
519
+ 1. Choose a model in the Demo tab.
520
+ 2. Select an example or paste your own text.
521
+ 3. Run extraction.
522
+ 4. Review highlighted spans and the summary table.
523
+
524
+ **Note**
525
+ Descriptions shown for models are placeholders and can be expanded into richer model cards later.
526
+ """)
527
+
528
+ return demo
529
+
530
+
531
+ # =========================================================
532
+ # Launch
533
+ # =========================================================
534
+
535
  if __name__ == "__main__":
536
+ app = build_ui_alt2() if LAYOUT == "alt2" else build_ui_alt3()
537
  app.launch()