abdullahsamra commited on
Commit
f622d20
·
verified ·
1 Parent(s): 07c3082

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. README.md +36 -6
  2. __pycache__/app.cpython-310.pyc +0 -0
  3. app.py +325 -0
  4. requirements.txt +4 -0
README.md CHANGED
@@ -1,12 +1,42 @@
1
  ---
2
- title: Egfr Ai Demo
3
- emoji: 🚀
4
- colorFrom: purple
5
- colorTo: indigo
6
  sdk: gradio
7
- sdk_version: 6.13.0
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: EGFR AI Demo
3
+ emoji: 🧪
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: gradio
 
7
  app_file: app.py
8
  pinned: false
9
+ license: other
10
  ---
11
 
12
+ # EGFR AI Demo
13
+
14
+ Private Gradio demo for the EGFR AI backend workflow.
15
+
16
+ This Space is a CPU demo version.
17
+
18
+ It demonstrates:
19
+
20
+ - SMILES property calculation
21
+ - response schema for generated molecules
22
+ - `pred_pAff_mean` field placeholder / integration point
23
+ - docking columns and backend contract
24
+ - example response from the private artifact bundle
25
+
26
+ ## Docking note
27
+
28
+ Real AutoDock-GPU docking is not enabled in this CPU Space.
29
+
30
+ The backend schema already supports:
31
+
32
+ - `docking_score`
33
+ - `docking_status`
34
+ - `docking_pose_file`
35
+
36
+ Supported docking modes in the full backend:
37
+
38
+ - `off`
39
+ - `top_k`
40
+ - `all`
41
+
42
+ In this CPU demo Space, docking status is returned as `not_run`.
__pycache__/app.cpython-310.pyc ADDED
Binary file (8 kB). View file
 
app.py ADDED
@@ -0,0 +1,325 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+
5
+ import gradio as gr
6
+ import pandas as pd
7
+ from huggingface_hub import snapshot_download
8
+
9
+ from rdkit import Chem
10
+ from rdkit.Chem import Descriptors, Crippen, Lipinski, rdMolDescriptors, QED
11
+
12
+
13
+ BUNDLE_REPO = os.getenv("BUNDLE_REPO", "abdullahsamra/egfr-ai-bundle")
14
+ HF_TOKEN = os.getenv("HF_TOKEN")
15
+
16
+ CACHE_DIR = Path("bundle_cache")
17
+ EXAMPLE_JSON = None
18
+ API_SPEC_TEXT = None
19
+ BACKEND_CONTRACT_TEXT = None
20
+
21
+
22
+ def download_bundle_light():
23
+ global EXAMPLE_JSON, API_SPEC_TEXT, BACKEND_CONTRACT_TEXT
24
+
25
+ try:
26
+ local_dir = snapshot_download(
27
+ repo_id=BUNDLE_REPO,
28
+ repo_type="dataset",
29
+ token=HF_TOKEN,
30
+ allow_patterns=[
31
+ "examples/generate_response.example.json",
32
+ "docs/API_RESPONSE_SPEC.md",
33
+ "configs/backend_contract.json",
34
+ "README.md",
35
+ ],
36
+ local_dir=str(CACHE_DIR),
37
+ )
38
+
39
+ root = Path(local_dir)
40
+
41
+ example_path = root / "examples" / "generate_response.example.json"
42
+ spec_path = root / "docs" / "API_RESPONSE_SPEC.md"
43
+ contract_path = root / "configs" / "backend_contract.json"
44
+
45
+ if example_path.exists():
46
+ EXAMPLE_JSON = json.loads(example_path.read_text(encoding="utf-8"))
47
+
48
+ if spec_path.exists():
49
+ API_SPEC_TEXT = spec_path.read_text(encoding="utf-8")
50
+
51
+ if contract_path.exists():
52
+ BACKEND_CONTRACT_TEXT = contract_path.read_text(encoding="utf-8")
53
+
54
+ return True, f"Loaded docs/examples from {BUNDLE_REPO}"
55
+
56
+ except Exception as e:
57
+ return False, f"Could not load private bundle. Check HF_TOKEN secret and BUNDLE_REPO variable. Error: {e}"
58
+
59
+
60
+ def canonicalize(smiles: str):
61
+ mol = Chem.MolFromSmiles(str(smiles).strip())
62
+ if mol is None:
63
+ return None, None
64
+ return mol, Chem.MolToSmiles(mol, isomericSmiles=True)
65
+
66
+
67
+ def try_sa_score(mol):
68
+ try:
69
+ from rdkit.Contrib.SA_Score import sascorer
70
+ return round(float(sascorer.calculateScore(mol)), 4)
71
+ except Exception:
72
+ return None
73
+
74
+
75
+ def compute_row(smiles: str):
76
+ mol, can = canonicalize(smiles)
77
+
78
+ if mol is None:
79
+ return {
80
+ "SMILES": smiles,
81
+ "valid": False,
82
+ "canonical_smiles": None,
83
+ "mw": None,
84
+ "logp": None,
85
+ "tpsa": None,
86
+ "hbd": None,
87
+ "hba": None,
88
+ "rot_bonds": None,
89
+ "qed": None,
90
+ "sa_score": None,
91
+ "pred_pAff_mean": None,
92
+ "docking_score": None,
93
+ "docking_status": "not_run",
94
+ "docking_pose_file": None,
95
+ "note": "Invalid SMILES",
96
+ }
97
+
98
+ return {
99
+ "SMILES": smiles,
100
+ "valid": True,
101
+ "canonical_smiles": can,
102
+ "mw": round(float(Descriptors.MolWt(mol)), 4),
103
+ "logp": round(float(Crippen.MolLogP(mol)), 4),
104
+ "tpsa": round(float(rdMolDescriptors.CalcTPSA(mol)), 4),
105
+ "hbd": int(Lipinski.NumHDonors(mol)),
106
+ "hba": int(Lipinski.NumHAcceptors(mol)),
107
+ "rot_bonds": int(Lipinski.NumRotatableBonds(mol)),
108
+ "qed": round(float(QED.qed(mol)), 4),
109
+ "sa_score": try_sa_score(mol),
110
+ "pred_pAff_mean": None,
111
+ "docking_score": None,
112
+ "docking_status": "not_run",
113
+ "docking_pose_file": None,
114
+ "note": "CPU demo: DeepPurpose and real docking are disabled in this Space.",
115
+ }
116
+
117
+
118
+ def score_smiles(smiles_text):
119
+ smiles_list = []
120
+ for part in str(smiles_text).replace(",", "\n").splitlines():
121
+ part = part.strip()
122
+ if part:
123
+ smiles_list.append(part)
124
+
125
+ if not smiles_list:
126
+ return pd.DataFrame(), {
127
+ "status": "error",
128
+ "message": "Please enter at least one SMILES.",
129
+ }
130
+
131
+ rows = [compute_row(s) for s in smiles_list]
132
+ df = pd.DataFrame(rows)
133
+
134
+ response = {
135
+ "status": "completed",
136
+ "mode": "score_smiles_cpu_demo",
137
+ "docking_mode": "off",
138
+ "summary": {
139
+ "num_input": len(smiles_list),
140
+ "num_valid": int(df["valid"].sum()),
141
+ "num_returned": len(df),
142
+ },
143
+ "columns": list(df.columns),
144
+ "results": rows,
145
+ "warnings": [
146
+ "This Space is a CPU demo.",
147
+ "DeepPurpose pred_pAff_mean is not computed in this Space version.",
148
+ "Real docking is not run in this CPU Space; docking_status is not_run.",
149
+ "Outputs are computational predictions only.",
150
+ ],
151
+ }
152
+
153
+ return df, response
154
+
155
+
156
+ def get_example_table():
157
+ if EXAMPLE_JSON and "results" in EXAMPLE_JSON:
158
+ return pd.DataFrame(EXAMPLE_JSON["results"])
159
+ return pd.DataFrame([
160
+ {"message": "Example response not loaded. Check HF_TOKEN secret or BUNDLE_REPO variable."}
161
+ ])
162
+
163
+
164
+ def get_example_json():
165
+ if EXAMPLE_JSON:
166
+ return EXAMPLE_JSON
167
+ return {
168
+ "status": "not_loaded",
169
+ "message": "Example response not loaded. Check HF_TOKEN secret or BUNDLE_REPO variable.",
170
+ }
171
+
172
+
173
+ def get_api_spec():
174
+ return API_SPEC_TEXT or "API_RESPONSE_SPEC.md not loaded. Check HF_TOKEN secret or BUNDLE_REPO variable."
175
+
176
+
177
+ def get_contract():
178
+ if BACKEND_CONTRACT_TEXT:
179
+ try:
180
+ return json.loads(BACKEND_CONTRACT_TEXT)
181
+ except Exception:
182
+ return {"raw": BACKEND_CONTRACT_TEXT}
183
+
184
+ return {
185
+ "status": "not_loaded",
186
+ "message": "backend_contract.json not loaded. Check HF_TOKEN secret or BUNDLE_REPO variable.",
187
+ }
188
+
189
+
190
+ loaded_ok, loaded_msg = download_bundle_light()
191
+
192
+
193
+ with gr.Blocks(title="EGFR AI Demo") as demo:
194
+ gr.Markdown(
195
+ """
196
+ # EGFR AI Demo
197
+
198
+ Private CPU demo for the EGFR AI backend workflow.
199
+
200
+ This demo shows the backend response shape for generation, scoring, filtering, and docking integration.
201
+
202
+ Real AutoDock-GPU docking is disabled in this CPU Space, but docking fields are present so backend/frontend teams can implement database columns and filters.
203
+ """
204
+ )
205
+
206
+ gr.Markdown(f"Bundle load status: {loaded_msg}")
207
+
208
+ with gr.Tab("Score SMILES"):
209
+ gr.Markdown(
210
+ """
211
+ Enter one or more SMILES.
212
+
213
+ This CPU demo calculates molecular properties and returns the same response columns expected by the backend.
214
+
215
+ Important:
216
+ - pred_pAff_mean is an integration field in this Space version.
217
+ - real docking is disabled in this CPU Space.
218
+ - docking_status is returned as not_run.
219
+ """
220
+ )
221
+
222
+ smiles_input = gr.Textbox(
223
+ label="SMILES input",
224
+ value="CCO\nc1ccccc1",
225
+ lines=5,
226
+ )
227
+
228
+ score_btn = gr.Button("Score SMILES")
229
+ score_table = gr.Dataframe(label="Results table")
230
+ score_json = gr.JSON(label="Backend-style JSON response")
231
+
232
+ score_btn.click(
233
+ score_smiles,
234
+ inputs=smiles_input,
235
+ outputs=[score_table, score_json],
236
+ )
237
+
238
+ with gr.Tab("Example Generate Response"):
239
+ gr.Markdown(
240
+ """
241
+ This example is loaded from the private Hugging Face Dataset bundle.
242
+
243
+ It shows the expected response shape after:
244
+
245
+ Generate -> Enrich -> Optional Docking
246
+ """
247
+ )
248
+
249
+ example_btn = gr.Button("Load example response")
250
+ example_table = gr.Dataframe(label="Example results table")
251
+ example_json = gr.JSON(label="Example JSON response")
252
+
253
+ example_btn.click(
254
+ lambda: (get_example_table(), get_example_json()),
255
+ outputs=[example_table, example_json],
256
+ )
257
+
258
+ with gr.Tab("Docking Contract"):
259
+ gr.Markdown(
260
+ """
261
+ ## Docking modes
262
+
263
+ The full backend supports:
264
+
265
+ - off
266
+ - top_k
267
+ - all
268
+
269
+ In this CPU Space, real AutoDock-GPU docking is disabled.
270
+
271
+ Returned docking fields in CPU demo:
272
+
273
+ - docking_score: null
274
+ - docking_status: not_run
275
+ - docking_pose_file: null
276
+
277
+ Recommended database columns:
278
+
279
+ - docking_score FLOAT NULL
280
+ - docking_status VARCHAR(30) DEFAULT 'not_run'
281
+ - docking_pose_file TEXT NULL
282
+ - docking_mode VARCHAR(20) DEFAULT 'off'
283
+ - docking_error TEXT NULL
284
+
285
+ Recommended frontend and filter behavior:
286
+
287
+ - Show all valid molecules: valid == true
288
+ - Show docked only: docking_status == completed
289
+ - Show not-yet-docked molecules: docking_status == not_run
290
+ - Sort docking results ascending, because more negative docking scores are usually better
291
+ """
292
+ )
293
+
294
+ contract_btn = gr.Button("Load backend contract")
295
+ contract_json = gr.JSON(label="backend_contract.json")
296
+
297
+ contract_btn.click(
298
+ get_contract,
299
+ outputs=contract_json,
300
+ )
301
+
302
+ with gr.Tab("API Spec"):
303
+ gr.Markdown(
304
+ """
305
+ This tab loads the API response specification from the private artifact bundle.
306
+
307
+ It explains the expected backend/frontend response format for:
308
+
309
+ - generated molecules
310
+ - scored SMILES
311
+ - docking fields
312
+ - frontend filters
313
+ """
314
+ )
315
+
316
+ spec_btn = gr.Button("Load API response spec")
317
+ spec_output = gr.Markdown(label="API_RESPONSE_SPEC.md")
318
+
319
+ spec_btn.click(
320
+ get_api_spec,
321
+ outputs=spec_output,
322
+ )
323
+
324
+
325
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio
2
+ pandas
3
+ huggingface_hub
4
+ rdkit-pypi