lighteternal commited on
Commit
ffb6de7
·
verified ·
1 Parent(s): 2bb3b76

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. __pycache__/app.cpython-310.pyc +0 -0
  2. app.py +76 -64
__pycache__/app.cpython-310.pyc CHANGED
Binary files a/__pycache__/app.cpython-310.pyc and b/__pycache__/app.cpython-310.pyc differ
 
app.py CHANGED
@@ -35,6 +35,8 @@ CSS = """
35
  --accent-deep: #0c4641;
36
  --accent-soft: #dbeee8;
37
  --accent-warm: #b8643d;
 
 
38
  --accent-warm-soft: #f3e1d5;
39
  --line: #c6cdbf;
40
  --warning: #8a4b0f;
@@ -122,13 +124,6 @@ CSS = """
122
  margin-top: 0.45rem;
123
  }
124
 
125
- .panel-note {
126
- border-left: 4px solid var(--accent);
127
- background: rgba(19,90,82,0.06);
128
- padding: 0.9rem 1rem;
129
- border-radius: 12px;
130
- }
131
-
132
  .metric-strip {
133
  display: grid;
134
  grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -140,7 +135,7 @@ CSS = """
140
  background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(248,244,236,0.9));
141
  padding: 0.8rem 0.9rem;
142
  border-radius: 18px;
143
- min-height: 7.5rem;
144
  box-shadow: 0 8px 24px rgba(23,37,45,0.04);
145
  }
146
 
@@ -311,11 +306,32 @@ CSS = """
311
 
312
  .gradio-container input,
313
  .gradio-container textarea,
314
- .gradio-container select {
 
 
 
 
 
 
 
315
  background: #fffdf8 !important;
316
  border: 1px solid #bfc8bf !important;
317
  border-radius: 14px !important;
318
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.7) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  }
320
 
321
  .gradio-container input:focus,
@@ -336,14 +352,14 @@ CSS = """
336
  }
337
 
338
  .gradio-container button.primary {
339
- background: linear-gradient(180deg, var(--accent), var(--accent-deep)) !important;
340
  color: #f7fbfa !important;
341
  border: none !important;
342
- box-shadow: 0 12px 24px rgba(22,91,85,0.18) !important;
343
  }
344
 
345
  .gradio-container button.secondary {
346
- background: linear-gradient(180deg, #f3e3d6, #edd9ca) !important;
347
  color: var(--ink) !important;
348
  border: 1px solid #dbc4b4 !important;
349
  }
@@ -458,6 +474,9 @@ EXAMPLES = {
458
  },
459
  }
460
 
 
 
 
461
 
462
  def _parse_smiles_text(value: str | None) -> list[str]:
463
  if not value:
@@ -715,19 +734,19 @@ with gr.Blocks(title="BioAssayAlign Compatibility Explorer", analytics_enabled=F
715
  <div id="hero">
716
  <div class="hero-grid">
717
  <div>
718
- <div class="eyebrow">BioAssayAlign · assay-conditioned molecule ranking</div>
719
- <div class="hero-title">Triage a candidate library against an assay concept</div>
720
  <div class="hero-copy">
721
- Write the assay the way a scientist would describe it, submit candidate SMILES, and get a ranked shortlist from the current BioAssayAlign compatibility model.
722
- The output is designed for practical prioritization, not generic search.
723
  </div>
724
  </div>
725
  <div class="hero-side">
726
- <div class="hero-side-title">What this Space is good at</div>
727
  <ul class="hero-list">
728
- <li>ranking a shortlist for one assay at a time</li>
729
- <li>comparing molecules relative to the same submitted list</li>
730
- <li>surfacing invalid or malformed SMILES immediately</li>
731
  </ul>
732
  </div>
733
  </div>
@@ -736,21 +755,13 @@ with gr.Blocks(title="BioAssayAlign Compatibility Explorer", analytics_enabled=F
736
  )
737
 
738
  with gr.Row():
739
- with gr.Column(scale=5):
740
- gr.HTML(
741
- """
742
- <div class="panel-note">
743
- Use structured assay fields when possible. Missing fields are allowed, but species, readout, format, and target metadata usually improve ranking quality.
744
- </div>
745
- """
746
- )
747
- with gr.Column(scale=4):
748
  gr.HTML(
749
  f"""
750
  <div class="metric-strip">
751
- <div class="metric-card"><span>Default model</span><strong>{MODEL_REPO_ID}</strong></div>
752
- <div class="metric-card"><span>Use the output for</span><strong>ranking, not probability</strong></div>
753
- <div class="metric-card"><span>Interactive cap</span><strong>{MAX_INPUT_SMILES} molecules</strong></div>
754
  </div>
755
  """
756
  )
@@ -758,9 +769,9 @@ Use structured assay fields when possible. Missing fields are allowed, but speci
758
  gr.HTML(
759
  """
760
  <div class="guide-grid">
761
- <div class="guide-card"><strong>1. Describe the assay</strong>Use normal lab language. Add UniProt, readout, format, and organism when known.</div>
762
- <div class="guide-card"><strong>2. Load candidate chemistry</strong>Paste one SMILES per line or upload a CSV with a <code>smiles</code> column.</div>
763
- <div class="guide-card"><strong>3. Interpret list-relative rank</strong>Read <em>priority</em> and <em>relative score</em> first. Treat raw score as a debug signal only.</div>
764
  </div>
765
  """
766
  )
@@ -773,29 +784,29 @@ Use structured assay fields when possible. Missing fields are allowed, but speci
773
  <div class="pane-header">
774
  <div>
775
  <div class="pane-kicker">Assay definition</div>
776
- <div class="pane-title">Describe the biological question</div>
777
  </div>
778
  </div>
779
- <div class="pane-copy">The model performs best when the assay text is concrete and metadata is explicit. Missing fields are allowed.</div>
780
  <div class="helper-row">
781
- <div class="helper-chip"><strong>Use plain lab language</strong>Focus on readout, target, cell system, or assay format.</div>
782
- <div class="helper-chip"><strong>Add UniProt when known</strong>Target identifiers usually sharpen the rank order.</div>
783
- <div class="helper-chip"><strong>One assay at a time</strong>Scores are meant to compare molecules within the same submitted list.</div>
784
  </div>
785
  """
786
  )
787
- example_name = gr.Dropdown(choices=list(EXAMPLES.keys()), value="JAK2 cell assay", label="Live example")
788
- gr.HTML("<div class='examples-note'>Examples run against the live model. They are not screenshots or mocked outputs.</div>")
789
- load_example_btn = gr.Button("Load live example", variant="secondary")
790
- assay_title = gr.Textbox(label="Assay title")
791
- description = gr.Textbox(label="Description", lines=6, placeholder="Describe the assay in practical lab language.")
792
  with gr.Row():
793
- organism = gr.Textbox(label="Organism", placeholder="Homo sapiens")
794
- readout = gr.Textbox(label="Readout", placeholder="binding / fluorescence / luminescence")
795
  with gr.Row():
796
- assay_format = gr.Textbox(label="Assay format", placeholder="biochemical / cell-based")
797
- assay_type = gr.Textbox(label="Assay type", placeholder="binding / inhibition / activation")
798
- target_uniprot = gr.Textbox(label="Target UniProt IDs", placeholder="Q06187, P52333")
799
 
800
  with gr.Column(scale=5, elem_classes="pane"):
801
  gr.HTML(
@@ -803,22 +814,23 @@ Use structured assay fields when possible. Missing fields are allowed, but speci
803
  <div class="pane-header">
804
  <div>
805
  <div class="pane-kicker">Candidate set</div>
806
- <div class="pane-title">Submit the molecules you care about</div>
807
  </div>
808
  </div>
809
- <div class="pane-copy">The model ranks molecules relative to this exact submitted list. For interactive use, keep the batch moderate and chemically sensible.</div>
810
  """
811
  )
812
  smiles_text = gr.Textbox(
813
  label="Candidate SMILES",
 
814
  lines=14,
815
  placeholder="Paste one candidate molecule per line. Example: CCO",
816
  )
817
  upload_file = gr.File(label="Upload CSV / TXT / SMI", file_count="single", file_types=[".csv", ".txt", ".smi", ".smiles"])
818
  top_k = gr.Slider(label="Top-K rows to display", minimum=5, maximum=200, step=5, value=DEFAULT_TOP_K)
819
- gr.HTML("<div class='section-note'>Tip: prefer parent or neutralized molecules, one SMILES per row, and avoid obvious salts or malformed fragments.</div>")
820
  with gr.Row(elem_classes="action-row"):
821
- run_btn = gr.Button("Run ranking", variant="primary")
822
  clear_btn = gr.ClearButton(value="Clear inputs", components=[assay_title, description, organism, readout, assay_format, assay_type, target_uniprot, smiles_text, upload_file])
823
 
824
  gr.HTML(
@@ -827,13 +839,13 @@ Use structured assay fields when possible. Missing fields are allowed, but speci
827
  <div class="pane-header">
828
  <div>
829
  <div class="pane-kicker">Output</div>
830
- <div class="pane-title">Interpret the shortlist</div>
831
  </div>
832
  </div>
833
  <div class="explain-grid">
834
- <div class="explain-card"><strong>Priority</strong>High-level triage label for the submitted list. Start here.</div>
835
- <div class="explain-card"><strong>Relative score</strong>0–100 scale within your submitted list only. It is not calibrated across unrelated lists.</div>
836
- <div class="explain-card"><strong>Raw model score</strong>Useful for debugging or exporting, but not the first thing a scientist should read.</div>
837
  </div>
838
  </div>
839
  """
@@ -863,17 +875,17 @@ Use structured assay fields when possible. Missing fields are allowed, but speci
863
  <div class="pane-header">
864
  <div>
865
  <div class="pane-kicker">Practical guidance</div>
866
- <div class="pane-title">How to use this interface well</div>
867
  </div>
868
  </div>
869
  <div class="explain-grid">
870
- <div class="explain-card"><strong>Write the assay like a scientist</strong>Use concrete descriptions, not keywords alone. If the assay is target-defined, add UniProt.</div>
871
- <div class="explain-card"><strong>Use clean candidate input</strong>Prefer parent or neutralized molecules. Upload a CSV with a <code>smiles</code> column if you have a larger list.</div>
872
- <div class="explain-card"><strong>Interpret results as triage</strong>Use priority and relative score first. Treat the raw model score as an internal ranking value, not a probability.</div>
873
  </div>
874
  <div class="footer-note" style="margin-top: 1rem;">
875
- This Space is tuned for shortlist ranking. It is not a generative chemistry tool, not a medicinal chemistry oracle, and not a wet-lab substitute.
876
- If you need a larger workflow, export the CSV and pass the ranked list into your own screening pipeline.
877
  </div>
878
  </div>
879
  """
 
35
  --accent-deep: #0c4641;
36
  --accent-soft: #dbeee8;
37
  --accent-warm: #b8643d;
38
+ --accent-bright: #d06a3b;
39
+ --accent-bright-deep: #b25124;
40
  --accent-warm-soft: #f3e1d5;
41
  --line: #c6cdbf;
42
  --warning: #8a4b0f;
 
124
  margin-top: 0.45rem;
125
  }
126
 
 
 
 
 
 
 
 
127
  .metric-strip {
128
  display: grid;
129
  grid-template-columns: repeat(3, minmax(0, 1fr));
 
135
  background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(248,244,236,0.9));
136
  padding: 0.8rem 0.9rem;
137
  border-radius: 18px;
138
+ min-height: 6.4rem;
139
  box-shadow: 0 8px 24px rgba(23,37,45,0.04);
140
  }
141
 
 
306
 
307
  .gradio-container input,
308
  .gradio-container textarea,
309
+ .gradio-container select,
310
+ .gradio-container [role="combobox"],
311
+ .gradio-container [role="listbox"],
312
+ .gradio-container [role="option"],
313
+ .gradio-container .choices,
314
+ .gradio-container .choices__inner,
315
+ .gradio-container .choices__list,
316
+ .gradio-container .choices__item {
317
  background: #fffdf8 !important;
318
  border: 1px solid #bfc8bf !important;
319
  border-radius: 14px !important;
320
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.7) !important;
321
+ color: var(--ink) !important;
322
+ }
323
+
324
+ .gradio-container [role="listbox"],
325
+ .gradio-container .choices__list,
326
+ .gradio-container .choices__item {
327
+ background: #fffdf8 !important;
328
+ color: var(--ink) !important;
329
+ }
330
+
331
+ .gradio-container [role="option"][aria-selected="true"],
332
+ .gradio-container .choices__item--selectable.is-highlighted {
333
+ background: #e7f0ed !important;
334
+ color: #0d3d38 !important;
335
  }
336
 
337
  .gradio-container input:focus,
 
352
  }
353
 
354
  .gradio-container button.primary {
355
+ background: linear-gradient(180deg, var(--accent-bright), var(--accent-bright-deep)) !important;
356
  color: #f7fbfa !important;
357
  border: none !important;
358
+ box-shadow: 0 14px 26px rgba(184,100,61,0.24) !important;
359
  }
360
 
361
  .gradio-container button.secondary {
362
+ background: linear-gradient(180deg, #f6e7da, #f0ddcd) !important;
363
  color: var(--ink) !important;
364
  border: 1px solid #dbc4b4 !important;
365
  }
 
474
  },
475
  }
476
 
477
+ DEFAULT_EXAMPLE_NAME = "JAK2 cell assay"
478
+ DEFAULT_EXAMPLE = EXAMPLES[DEFAULT_EXAMPLE_NAME]
479
+
480
 
481
  def _parse_smiles_text(value: str | None) -> list[str]:
482
  if not value:
 
734
  <div id="hero">
735
  <div class="hero-grid">
736
  <div>
737
+ <div class="eyebrow">BioAssayAlign · assay-conditioned compound ranking</div>
738
+ <div class="hero-title">Rank a compound list against one assay definition</div>
739
  <div class="hero-copy">
740
+ Define an assay in structured scientific terms, submit a candidate molecule set, and obtain a within-list ranking from the current BioAssayAlign compatibility model.
741
+ The output is intended for shortlist prioritization, retrospective analysis, and assay-conditioned compound triage.
742
  </div>
743
  </div>
744
  <div class="hero-side">
745
+ <div class="hero-side-title">Operational scope</div>
746
  <ul class="hero-list">
747
+ <li>Single-assay compound ranking with explicit assay metadata</li>
748
+ <li>Relative prioritization within the submitted molecule set</li>
749
+ <li>Immediate rejection of malformed or non-parseable SMILES</li>
750
  </ul>
751
  </div>
752
  </div>
 
755
  )
756
 
757
  with gr.Row():
758
+ with gr.Column(scale=9):
 
 
 
 
 
 
 
 
759
  gr.HTML(
760
  f"""
761
  <div class="metric-strip">
762
+ <div class="metric-card"><span>Model</span><strong>Qwen3-Embedding-0.6B compatibility scorer</strong></div>
763
+ <div class="metric-card"><span>Input contract</span><strong>1 assay definition + up to {MAX_INPUT_SMILES} candidate SMILES</strong></div>
764
+ <div class="metric-card"><span>Score semantics</span><strong>Use rank and relative score within the submitted list</strong></div>
765
  </div>
766
  """
767
  )
 
769
  gr.HTML(
770
  """
771
  <div class="guide-grid">
772
+ <div class="guide-card"><strong>1. Specify the assay</strong>Include target, readout, organism, and format when known. Missing fields are allowed but reduce context.</div>
773
+ <div class="guide-card"><strong>2. Provide a candidate set</strong>Paste one SMILES per line or upload a CSV with a <code>smiles</code> column.</div>
774
+ <div class="guide-card"><strong>3. Read the output scientifically</strong>Use rank and relative score for shortlist decisions. Raw model score is an internal compatibility logit.</div>
775
  </div>
776
  """
777
  )
 
784
  <div class="pane-header">
785
  <div>
786
  <div class="pane-kicker">Assay definition</div>
787
+ <div class="pane-title">Define the assay context</div>
788
  </div>
789
  </div>
790
+ <div class="pane-copy">The model performs best when the assay description is concrete and metadata fields are explicit. This form is preloaded with a live example.</div>
791
  <div class="helper-row">
792
+ <div class="helper-chip"><strong>Describe the protocol signal</strong>State the readout, assay system, target biology, and measurement context.</div>
793
+ <div class="helper-chip"><strong>Use target identifiers</strong>UniProt IDs often improve separation between plausible and implausible candidates.</div>
794
+ <div class="helper-chip"><strong>Interpret scores locally</strong>Ranking is meaningful within the submitted library, not across unrelated runs.</div>
795
  </div>
796
  """
797
  )
798
+ example_name = gr.Dropdown(choices=list(EXAMPLES.keys()), value=DEFAULT_EXAMPLE_NAME, label="Example assay")
799
+ gr.HTML("<div class='examples-note'>The form is initialized with a live JAK2 example. Selecting another example will overwrite the current fields.</div>")
800
+ load_example_btn = gr.Button("Replace with selected example", variant="secondary")
801
+ assay_title = gr.Textbox(label="Assay title", value=DEFAULT_EXAMPLE["title"])
802
+ description = gr.Textbox(label="Description", value=DEFAULT_EXAMPLE["description"], lines=6, placeholder="Describe the assay in practical lab language.")
803
  with gr.Row():
804
+ organism = gr.Textbox(label="Organism", value=DEFAULT_EXAMPLE["organism"], placeholder="Homo sapiens")
805
+ readout = gr.Textbox(label="Readout", value=DEFAULT_EXAMPLE["readout"], placeholder="binding / fluorescence / luminescence")
806
  with gr.Row():
807
+ assay_format = gr.Textbox(label="Assay format", value=DEFAULT_EXAMPLE["assay_format"], placeholder="biochemical / cell-based")
808
+ assay_type = gr.Textbox(label="Assay type", value=DEFAULT_EXAMPLE["assay_type"], placeholder="binding / inhibition / activation")
809
+ target_uniprot = gr.Textbox(label="Target UniProt IDs", value=DEFAULT_EXAMPLE["target_uniprot"], placeholder="Q06187, P52333")
810
 
811
  with gr.Column(scale=5, elem_classes="pane"):
812
  gr.HTML(
 
814
  <div class="pane-header">
815
  <div>
816
  <div class="pane-kicker">Candidate set</div>
817
+ <div class="pane-title">Submit the candidate molecules</div>
818
  </div>
819
  </div>
820
+ <div class="pane-copy">The model ranks compounds relative to this exact submitted set. Use parent or cleaned molecules when possible.</div>
821
  """
822
  )
823
  smiles_text = gr.Textbox(
824
  label="Candidate SMILES",
825
+ value=DEFAULT_EXAMPLE["smiles"],
826
  lines=14,
827
  placeholder="Paste one candidate molecule per line. Example: CCO",
828
  )
829
  upload_file = gr.File(label="Upload CSV / TXT / SMI", file_count="single", file_types=[".csv", ".txt", ".smi", ".smiles"])
830
  top_k = gr.Slider(label="Top-K rows to display", minimum=5, maximum=200, step=5, value=DEFAULT_TOP_K)
831
+ gr.HTML("<div class='section-note'>Use one SMILES per row. Invalid or non-standardizable structures are flagged separately and excluded from ranking.</div>")
832
  with gr.Row(elem_classes="action-row"):
833
+ run_btn = gr.Button("Run assay-conditioned ranking", variant="primary")
834
  clear_btn = gr.ClearButton(value="Clear inputs", components=[assay_title, description, organism, readout, assay_format, assay_type, target_uniprot, smiles_text, upload_file])
835
 
836
  gr.HTML(
 
839
  <div class="pane-header">
840
  <div>
841
  <div class="pane-kicker">Output</div>
842
+ <div class="pane-title">Interpret the ranked shortlist</div>
843
  </div>
844
  </div>
845
  <div class="explain-grid">
846
+ <div class="explain-card"><strong>Priority band</strong>Use this as the first-pass triage signal when scanning the ranked list.</div>
847
+ <div class="explain-card"><strong>Relative score</strong>0–100 scale computed within the submitted list only. It is not calibrated across separate runs.</div>
848
+ <div class="explain-card"><strong>Model score</strong>Internal compatibility logit from the scorer head. Useful for export or debugging, not for direct biological interpretation.</div>
849
  </div>
850
  </div>
851
  """
 
875
  <div class="pane-header">
876
  <div>
877
  <div class="pane-kicker">Practical guidance</div>
878
+ <div class="pane-title">Operating guidance</div>
879
  </div>
880
  </div>
881
  <div class="explain-grid">
882
+ <div class="explain-card"><strong>Encode the assay explicitly</strong>Use full scientific language rather than keywords alone. Include target and readout when available.</div>
883
+ <div class="explain-card"><strong>Provide chemically reasonable candidates</strong>Prefer parent or neutralized structures. Upload a CSV with a <code>smiles</code> column for larger lists.</div>
884
+ <div class="explain-card"><strong>Use the output as a ranking signal</strong>Priority and relative score are the intended decision aids. Model score is not a calibrated success probability.</div>
885
  </div>
886
  <div class="footer-note" style="margin-top: 1rem;">
887
+ This Space is intended for assay-conditioned shortlist ranking. It is not a generative chemistry system and it does not replace confirmatory screening.
888
+ Export the ranked CSV if you want to continue downstream analysis in your own workflow.
889
  </div>
890
  </div>
891
  """