chembfn-webui 1.2.3__tar.gz → 2.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/PKG-INFO +19 -6
  2. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/README.md +18 -6
  3. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui/bin/app.py +131 -54
  4. chembfn_webui-2.1.3/chembfn_webui/bin/favicon.png +0 -0
  5. chembfn_webui-2.1.3/chembfn_webui/cache/results.csv +1 -0
  6. chembfn_webui-2.1.3/chembfn_webui/lib/utilities.py +419 -0
  7. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui/lib/version.py +1 -1
  8. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui.egg-info/PKG-INFO +19 -6
  9. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui.egg-info/SOURCES.txt +3 -1
  10. chembfn_webui-2.1.3/chembfn_webui.egg-info/requires.txt +5 -0
  11. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/setup.py +35 -12
  12. chembfn_webui-2.1.3/test/test_prompt_parsing.py +82 -0
  13. chembfn_webui-2.1.3/test/test_user_expression_validator.py +138 -0
  14. chembfn_webui-1.2.3/chembfn_webui/bin/favicon.png +0 -0
  15. chembfn_webui-1.2.3/chembfn_webui/cache/results.csv +0 -1
  16. chembfn_webui-1.2.3/chembfn_webui/lib/utilities.py +0 -237
  17. chembfn_webui-1.2.3/chembfn_webui.egg-info/requires.txt +0 -5
  18. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/LICENSE +0 -0
  19. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui/__init__.py +0 -0
  20. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui/cache/cache_file_here.txt +0 -0
  21. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui/model/base_model/place_base_model_here.txt +0 -0
  22. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui/model/lora/place_lora_folder_here.txt +0 -0
  23. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui/model/standalone_model/place_standalone_model_folder_here.txt +0 -0
  24. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui/model/vocab/place_vocabulary_file_here.txt +0 -0
  25. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui.egg-info/dependency_links.txt +0 -0
  26. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui.egg-info/entry_points.txt +0 -0
  27. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/chembfn_webui.egg-info/top_level.txt +0 -0
  28. {chembfn_webui-1.2.3 → chembfn_webui-2.1.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chembfn_webui
3
- Version: 1.2.3
3
+ Version: 2.1.3
4
4
  Summary: WebUI for ChemBFN
5
5
  Home-page: https://github.com/Augus1999/ChemBFN-WebUI
6
6
  Author: Nianze A. Tao
@@ -15,15 +15,16 @@ Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
18
19
  Classifier: Topic :: Scientific/Engineering :: Chemistry
19
20
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
21
  Requires-Python: >=3.11
21
22
  Description-Content-Type: text/markdown
22
23
  License-File: LICENSE
23
- Requires-Dist: bayesianflow_for_chem>=2.2.2
24
- Requires-Dist: mol2chemfigPy3>=1.5.11
25
- Requires-Dist: gradio>=5.32.1
26
- Requires-Dist: torch>=2.7.0
24
+ Requires-Dist: bayesianflow_for_chem>=2.4.0
25
+ Requires-Dist: mol2chemfigPy3>=1.5.12
26
+ Requires-Dist: gradio<7.0.0,>=6.0.0
27
+ Requires-Dist: torch>=2.9.0
27
28
  Requires-Dist: selfies>=2.2.0
28
29
  Dynamic: author
29
30
  Dynamic: author-email
@@ -39,10 +40,20 @@ Dynamic: requires-dist
39
40
  Dynamic: requires-python
40
41
  Dynamic: summary
41
42
 
42
- ## ChemBFN WebUI: WebUI to generate and visualise molecules for ChemBFN method
43
+ ## Web-based UI visualisation tool for ChemBFN method
43
44
 
45
+ [![PyPI](https://img.shields.io/pypi/v/chembfn-webui?color=green)](https://pypi.org/project/chembfn-webui/)
46
+ ![CI](https://github.com/Augus1999/ChemBFN-WebUI/actions/workflows/pytest.yml/badge.svg)
47
+ ![black](https://img.shields.io/badge/code%20style-black-black)
48
+ [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)
44
49
 
45
50
 
51
+
52
+ > Important:
53
+ >
54
+ > For the security concerning, it is not recommended to use this application as a public service.
55
+ > When deploying on a local host as a shared application, it is better to install this application in a container or VM, to prevent this application from accessing the Internet, and to limit the premissions of read, create, and delete loacal files and directories.
56
+
46
57
  ### 1. Install
47
58
 
48
59
  ```bash
@@ -97,6 +108,7 @@ For example,
97
108
  > > "name": JOB_NAME
98
109
  > >}
99
110
  > >```
111
+ > >The configureation file for base models can be downloaded [here](https://huggingface.co/suenoomozawa/ChemBFN/resolve/main/config.json).
100
112
 
101
113
  If placed correctly, all these files can be seen in the "model explorer" tab.
102
114
 
@@ -146,6 +158,7 @@ Under "advanced control" tab
146
158
 
147
159
  * You can control semi-autoregressive behaviours by key in `F` for switching off SAR, `T` for switching on SAR, and prompt like `F,F,T,...` to individually control the SAR in an ensemble model.
148
160
  * You can add unwanted tokens, e.g., `[Cu],p,[Si]`.
161
+ * You can customise the result preprocessing function, e.g., the model output a reaction SMILES "CCI.C[O-]>>COCC" which couldn't be recognised by RDKit; you can pass `lambda x: x.split(">>")[-1]` to force the program only looking at the products.
149
162
 
150
163
  ### 6. Generate molecules
151
164
 
@@ -1,13 +1,23 @@
1
- ## ChemBFN WebUI: WebUI to generate and visualise molecules for ChemBFN method
1
+ ## Web-based UI visualisation tool for ChemBFN method
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/chembfn-webui?color=green)](https://pypi.org/project/chembfn-webui/)
4
+ ![CI](https://github.com/Augus1999/ChemBFN-WebUI/actions/workflows/pytest.yml/badge.svg)
5
+ ![black](https://img.shields.io/badge/code%20style-black-black)
6
+ [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)
2
7
 
3
8
  <p align="left">
4
- <img src="image/screenshot_0.jpeg" alt="screenshot 0" width="400" height="auto">
5
- <img src="image/screenshot_1.jpeg" alt="screenshot 1" width="400" height="auto">
6
- <img src="image/screenshot_2.jpeg" alt="screenshot 2" width="400" height="auto">
7
- <img src="image/screenshot_3.jpeg" alt="screenshot 3" width="400" height="auto">
8
- <img src="image/screenshot_4.jpeg" alt="screenshot 4" width="400" height="auto">
9
+ <img src="image/screenshot_0.jpeg" alt="screenshot 0" width="360" height="auto">
10
+ <img src="image/screenshot_1.jpeg" alt="screenshot 1" width="360" height="auto">
11
+ <img src="image/screenshot_2.jpeg" alt="screenshot 2" width="360" height="auto">
12
+ <img src="image/screenshot_3.jpeg" alt="screenshot 3" width="360" height="auto">
13
+ <img src="image/screenshot_4.jpeg" alt="screenshot 4" width="360" height="auto">
9
14
  </p>
10
15
 
16
+ > [!IMPORTANT]
17
+ >
18
+ > For the security concerning, it is not recommended to use this application as a public service.
19
+ > When deploying on a local host as a shared application, it is better to install this application in a container or VM, to prevent this application from accessing the Internet, and to limit the premissions of read, create, and delete loacal files and directories.
20
+
11
21
  ### 1. Install
12
22
 
13
23
  ```bash
@@ -62,6 +72,7 @@ For example,
62
72
  > > "name": JOB_NAME
63
73
  > >}
64
74
  > >```
75
+ > >The configureation file for base models can be downloaded [here](https://huggingface.co/suenoomozawa/ChemBFN/resolve/main/config.json).
65
76
 
66
77
  If placed correctly, all these files can be seen in the "model explorer" tab.
67
78
 
@@ -111,6 +122,7 @@ Under "advanced control" tab
111
122
 
112
123
  * You can control semi-autoregressive behaviours by key in `F` for switching off SAR, `T` for switching on SAR, and prompt like `F,F,T,...` to individually control the SAR in an ensemble model.
113
124
  * You can add unwanted tokens, e.g., `[Cu],p,[Si]`.
125
+ * You can customise the result preprocessing function, e.g., the model output a reaction SMILES "CCI.C[O-]>>COCC" which couldn't be recognised by RDKit; you can pass `lambda x: x.split(">>")[-1]` to force the program only looking at the products.
114
126
 
115
127
  ### 6. Generate molecules
116
128
 
@@ -6,8 +6,9 @@ Define application behaviours.
6
6
  import sys
7
7
  import argparse
8
8
  from pathlib import Path
9
+ from copy import deepcopy
9
10
  from functools import partial
10
- from typing import Tuple, List, Dict, Union, Literal
11
+ from typing import Tuple, List, Dict, Optional, Union, Literal
11
12
 
12
13
  sys.path.append(str(Path(__file__).parent.parent))
13
14
  from rdkit.Chem import Draw, MolFromSmiles # type: ignore
@@ -38,6 +39,7 @@ from lib.utilities import (
38
39
  parse_prompt,
39
40
  parse_exclude_token,
40
41
  parse_sar_control,
42
+ build_result_prep_fn,
41
43
  )
42
44
  from lib.version import __version__
43
45
 
@@ -45,7 +47,7 @@ vocabs = find_vocab()
45
47
  models = find_model()
46
48
  cache_dir = Path(__file__).parent.parent / "cache"
47
49
  favicon_dir = Path(__file__).parent / "favicon.png"
48
- _result_count = 0
50
+ _RESULT_COUNT = 0
49
51
 
50
52
  HTML_STYLE = gr.InputHTMLAttributes(
51
53
  autocapitalize="off",
@@ -76,7 +78,7 @@ def selfies2vec(sel: str, vocab_dict: Dict[str, int]) -> List[int]:
76
78
  return [vocab_dict.get(i, unknown_id) for i in s]
77
79
 
78
80
 
79
- def refresh(
81
+ def _refresh(
80
82
  model_selected: str, vocab_selected: str, tokeniser_selected: str
81
83
  ) -> Tuple[
82
84
  List[str], List[str], List[List[str]], List[List[str]], gr.Dropdown, gr.Dropdown
@@ -121,7 +123,7 @@ def refresh(
121
123
  return a, b, c, d, e, f
122
124
 
123
125
 
124
- def select_lora(evt: gr.SelectData, prompt: str) -> str:
126
+ def _select_lora(evt: gr.SelectData, prompt: str) -> str:
125
127
  """
126
128
  Select LoRA model name from Dataframe object.
127
129
 
@@ -141,7 +143,7 @@ def select_lora(evt: gr.SelectData, prompt: str) -> str:
141
143
  return f"{prompt};\n<{selected_lora}:1>"
142
144
 
143
145
 
144
- def token_name_change_evt(
146
+ def _token_name_change_evt(
145
147
  token_name: str, vocab_fn: str
146
148
  ) -> Tuple[gr.Dropdown, gr.Tab, gr.Tab]:
147
149
  """
@@ -178,14 +180,15 @@ def run(
178
180
  guidance_strength: float,
179
181
  method: Literal["BFN", "ODE"],
180
182
  temperature: float,
181
- prompt: str,
182
- scaffold: str,
183
- template: str,
184
- sar_control: str,
185
- exclude_token: str,
183
+ prompt: Optional[str],
184
+ scaffold: Optional[str],
185
+ template: Optional[str],
186
+ sar_control: Optional[str],
187
+ exclude_token: Optional[str],
186
188
  quantise: Literal["on", "off"],
187
189
  jited: Literal["on", "off"],
188
190
  sorted_: Literal["on", "off"],
191
+ result_prep_fn: Optional[str],
189
192
  ) -> Tuple[Union[List, None], List[str], str, gr.TextArea, str]:
190
193
  """
191
194
  Run generation or inpainting.
@@ -207,6 +210,7 @@ def run(
207
210
  :param quantise: `"on"` or `"off"`
208
211
  :param jited: `"on"` or `"off"`
209
212
  :param sorted\\_: whether to sort the reulst; `"on"` or `"off"`
213
+ :param result_prep_fn: a string form result preprocessing function
210
214
  :type model_name: str
211
215
  :type token_name: str
212
216
  :type vocab_fn: str
@@ -216,14 +220,15 @@ def run(
216
220
  :type guidance_strength: float
217
221
  :type method: str
218
222
  :type temperature: float
219
- :type prompt: str
220
- :type scaffold: str
221
- :type template: str
222
- :type sar_control: str
223
- :type exclude_token: str
223
+ :type prompt: str | None
224
+ :type scaffold: str | None
225
+ :type template: str | None
226
+ :type sar_control: str | None
227
+ :type exclude_token: str | None
224
228
  :type quantise: str
225
229
  :type jited: str
226
230
  :type sorted\\_: str
231
+ :type result_prep_fn: str | None
227
232
  :return: list of images \n
228
233
  list of generated molecules \n
229
234
  Chemfig code \n
@@ -239,6 +244,8 @@ def run(
239
244
  lora_label_dict = dict([[i[0], i[2] != []] for i in models["lora"]])
240
245
  standalone_lmax_dict = dict([[i[0], i[3]] for i in models["standalone"]])
241
246
  lora_lmax_dict = dict([[i[0], i[3]] for i in models["lora"]])
247
+ # ------- build result preprocessing function -------
248
+ _result_prep_fn = build_result_prep_fn(result_prep_fn)
242
249
  # ------- build tokeniser -------
243
250
  if token_name == "SMILES & SAFE":
244
251
  vocab_keys = VOCAB_KEYS
@@ -246,13 +253,13 @@ def run(
246
253
  trans_fn = lambda x: [i for i in x if (MolFromSmiles(i) and i)]
247
254
  img_fn = lambda x: [Draw.MolToImage(MolFromSmiles(i), (500, 500)) for i in x]
248
255
  chemfig_fn = lambda x: [mol2chemfig(i, "-r", inline=True) for i in x]
249
- if token_name == "FASTA":
256
+ elif token_name == "FASTA":
250
257
  vocab_keys = FASTA_VOCAB_KEYS
251
258
  tokeniser = fasta2vec
252
259
  trans_fn = lambda x: [i for i in x if i]
253
260
  img_fn = lambda _: None # senseless to provide dumb 2D images
254
261
  chemfig_fn = lambda _: [""] # senseless to provide very long Chemfig code
255
- if token_name == "SELFIES":
262
+ elif token_name == "SELFIES":
256
263
  vocab_data = load_vocab(vocabs[vocab_fn])
257
264
  vocab_keys = vocab_data["vocab_keys"]
258
265
  vocab_dict = vocab_data["vocab_dict"]
@@ -262,11 +269,15 @@ def run(
262
269
  Draw.MolToImage(MolFromSmiles(decoder(i)), (500, 500)) for i in x
263
270
  ]
264
271
  chemfig_fn = lambda x: [mol2chemfig(decoder(i), "-r", inline=True) for i in x]
272
+ else:
273
+ raise RuntimeError("Oops, maybe something wrong with Gradio.")
265
274
  _method = "bfn" if method == "BFN" else f"ode:{temperature}"
266
275
  # ------- build model -------
267
276
  prompt_info = parse_prompt(prompt)
268
277
  sar_flag = parse_sar_control(sar_control)
269
- print("Prompt summary:", prompt_info, "semi-autoregression:", sar_flag) # prompt
278
+ _info = deepcopy(prompt_info)
279
+ _info["semi-autoregression"] = deepcopy(sar_flag)
280
+ print("Prompt summary:", _info) # prompt
270
281
  if not prompt_info["lora"]:
271
282
  if model_name in base_model_dict:
272
283
  lmax = sequence_size
@@ -287,7 +298,7 @@ def run(
287
298
  mlp = MLP.from_checkpoint(
288
299
  standalone_model_dict[model_name] / "mlp.pt"
289
300
  )
290
- y = torch.tensor([prompt_info["objective"]], dtype=torch.float32)
301
+ y = torch.tensor([prompt_info["objective"][0]], dtype=torch.float32)
291
302
  y = mlp.forward(y)
292
303
  else:
293
304
  y = None
@@ -317,7 +328,7 @@ def run(
317
328
  mlp = MLP.from_checkpoint(
318
329
  lora_model_dict[prompt_info["lora"][0]] / "mlp.pt"
319
330
  )
320
- y = torch.tensor([prompt_info["objective"]], dtype=torch.float32)
331
+ y = torch.tensor([prompt_info["objective"][0]], dtype=torch.float32)
321
332
  y = mlp.forward(y)
322
333
  else:
323
334
  y = None
@@ -330,7 +341,7 @@ def run(
330
341
  if jited == "on":
331
342
  bfn.compile()
332
343
  else:
333
- lmax = max([lora_lmax_dict[i] for i in prompt_info["lora"]])
344
+ lmax = max(lora_lmax_dict[i] for i in prompt_info["lora"])
334
345
  if model_name in base_model_dict:
335
346
  base_model_dir = base_model_dict[model_name]
336
347
  else:
@@ -355,10 +366,15 @@ def run(
355
366
  if jited == "on":
356
367
  bfn.compile()
357
368
  _message.append(f"Sequence length set to {lmax} from model metadata.")
369
+ result_prep_fn_ = lambda x: [_result_prep_fn(i) for i in x]
358
370
  # ------- inference -------
359
371
  allowed_tokens = parse_exclude_token(exclude_token, vocab_keys)
360
372
  if not allowed_tokens:
361
373
  allowed_tokens = "all"
374
+ if scaffold is None:
375
+ scaffold = ""
376
+ if template is None:
377
+ template = ""
362
378
  scaffold = scaffold.strip()
363
379
  template = template.strip()
364
380
  if scaffold:
@@ -376,7 +392,7 @@ def run(
376
392
  allowed_tokens=allowed_tokens,
377
393
  sort=sorted_ == "on",
378
394
  )
379
- mols = trans_fn(mols)
395
+ mols = trans_fn(result_prep_fn_(mols))
380
396
  imgs = img_fn(mols)
381
397
  chemfigs = chemfig_fn(mols)
382
398
  if template:
@@ -396,7 +412,7 @@ def run(
396
412
  allowed_tokens=allowed_tokens,
397
413
  sort=sorted_ == "on",
398
414
  )
399
- mols = trans_fn(mols)
415
+ mols = trans_fn(result_prep_fn_(mols))
400
416
  imgs = img_fn(mols)
401
417
  chemfigs = chemfig_fn(mols)
402
418
  else:
@@ -412,17 +428,17 @@ def run(
412
428
  allowed_tokens=allowed_tokens,
413
429
  sort=sorted_ == "on",
414
430
  )
415
- mols = trans_fn(mols)
431
+ mols = trans_fn(result_prep_fn_(mols))
416
432
  imgs = img_fn(mols)
417
433
  chemfigs = chemfig_fn(mols)
418
- n_mol = len(mols)
419
434
  with open(cache_dir / "results.csv", "w", encoding="utf-8", newline="") as rf:
420
435
  rf.write("\n".join(mols))
421
436
  _message.append(
422
- f"{n_mol} smaples generated and saved to cache that can be downloaded."
437
+ f"{(n_mol := len(mols))} {'smaple' if n_mol in (0, 1) else 'samples'} "
438
+ "generated and saved to cache that can be downloaded."
423
439
  )
424
- global _result_count
425
- _result_count = n_mol
440
+ global _RESULT_COUNT
441
+ _RESULT_COUNT = n_mol
426
442
  return (
427
443
  imgs,
428
444
  mols,
@@ -432,14 +448,11 @@ def run(
432
448
  )
433
449
 
434
450
 
435
- with gr.Blocks(
436
- title="ChemBFN WebUI",
437
- css="footer {display: none !important} .custom_footer {text-align:center;bottom:0;}",
438
- analytics_enabled=False,
439
- ) as app:
451
+ with gr.Blocks(title="ChemBFN WebUI", analytics_enabled=False) as app:
440
452
  with gr.Row():
441
453
  with gr.Column(scale=1):
442
454
  btn = gr.Button("RUN", variant="primary")
455
+ stop = gr.Button("\u23f9", variant="stop", visible=False)
443
456
  model_name = gr.Dropdown(
444
457
  [i[0] for i in models["base"]] + [i[0] for i in models["standalone"]],
445
458
  label="model",
@@ -484,14 +497,15 @@ with gr.Blocks(
484
497
  message = gr.TextArea(label="message", lines=2)
485
498
  with gr.Tab(label="result viewer"):
486
499
  with gr.Tab(label="result"):
487
- btn_download = gr.File(label="download", visible=False)
500
+ btn_download = gr.File(
501
+ str(cache_dir / "results.csv"), label="download", visible=False
502
+ )
488
503
  result = gr.Dataframe(
489
504
  headers=["molecule"],
490
- col_count=(1, "fixed"),
505
+ column_count=(1, "fixed"),
491
506
  label="",
492
- show_fullscreen_button=True,
507
+ interactive=False,
493
508
  show_row_numbers=True,
494
- show_copy_button=True,
495
509
  )
496
510
  with gr.Tab(
497
511
  label="LATEX Chemfig", visible=token_name.value != "FASTA"
@@ -509,7 +523,7 @@ with gr.Blocks(
509
523
  vocab_table = gr.Dataframe(
510
524
  list(vocabs.keys()),
511
525
  headers=["name"],
512
- col_count=(1, "fixed"),
526
+ column_count=(1, "fixed"),
513
527
  label="",
514
528
  interactive=False,
515
529
  show_row_numbers=True,
@@ -518,7 +532,7 @@ with gr.Blocks(
518
532
  base_table = gr.Dataframe(
519
533
  [i[0] for i in models["base"]],
520
534
  headers=["name"],
521
- col_count=(1, "fixed"),
535
+ column_count=(1, "fixed"),
522
536
  label="",
523
537
  interactive=False,
524
538
  show_row_numbers=True,
@@ -527,7 +541,7 @@ with gr.Blocks(
527
541
  standalone_table = gr.Dataframe(
528
542
  [[i[0], i[2]] for i in models["standalone"]],
529
543
  headers=["name", "objective"],
530
- col_count=(2, "fixed"),
544
+ column_count=(2, "fixed"),
531
545
  label="",
532
546
  interactive=False,
533
547
  show_row_numbers=True,
@@ -536,7 +550,7 @@ with gr.Blocks(
536
550
  lora_tabel = gr.Dataframe(
537
551
  [[i[0], i[2]] for i in models["lora"]],
538
552
  headers=["name", "objective"],
539
- col_count=(2, "fixed"),
553
+ column_count=(2, "fixed"),
540
554
  label="",
541
555
  interactive=False,
542
556
  show_row_numbers=True,
@@ -553,17 +567,33 @@ with gr.Blocks(
553
567
  placeholder="key in unwanted tokens separated by comma.",
554
568
  html_attributes=HTML_STYLE,
555
569
  )
556
- quantise = gr.Radio(["on", "off"], value="off", label="quantisation")
557
- jited = gr.Radio(["on", "off"], value="off", label="JIT")
558
- sorted_ = gr.Radio(
559
- ["on", "off"],
560
- value="off",
561
- label="sort result",
562
- info="sorting based on entropy",
570
+ result_prep_fn = gr.Textbox(
571
+ "lambda x: x",
572
+ label="result preprocessing function",
573
+ placeholder="lambda x: x",
574
+ html_attributes=HTML_STYLE,
563
575
  )
576
+ with gr.Row(scale=1):
577
+ quantise = gr.Radio(
578
+ ["on", "off"], value="off", label="quantisation"
579
+ )
580
+ jited = gr.Radio(["on", "off"], value="off", label="JIT")
581
+ sorted_ = gr.Radio(
582
+ ["on", "off"], value="off", label="sort result based on entropy"
583
+ )
564
584
  gr.HTML(sys_info(), elem_classes="custom_footer", elem_id="footer")
565
585
  # ------ user interaction events -------
566
- btn.click(
586
+ gen = btn.click(
587
+ fn=lambda: (
588
+ gr.Button("RUN", variant="primary", visible=False),
589
+ gr.Button("\u23f9", variant="stop", visible=True),
590
+ ),
591
+ inputs=None,
592
+ outputs=[btn, stop],
593
+ api_name="switch_to_stop_mode",
594
+ api_description="Switch to STOP.",
595
+ api_visibility="private",
596
+ ).then(
567
597
  fn=run,
568
598
  inputs=[
569
599
  model_name,
@@ -583,11 +613,36 @@ with gr.Blocks(
583
613
  quantise,
584
614
  jited,
585
615
  sorted_,
616
+ result_prep_fn,
586
617
  ],
587
618
  outputs=[img, result, chemfig, message, btn_download],
619
+ api_name="run",
620
+ api_description="Run ChemBFN model.",
621
+ )
622
+ gen.then(
623
+ fn=lambda: (
624
+ gr.Button("RUN", variant="primary", visible=True),
625
+ gr.Button("\u23f9", variant="stop", visible=False),
626
+ ),
627
+ inputs=None,
628
+ outputs=[btn, stop],
629
+ api_name="switch_back_to_run_mode",
630
+ api_description="Swtch back to RUN.",
631
+ api_visibility="private",
632
+ )
633
+ stop.click(
634
+ fn=lambda: (
635
+ gr.Button("RUN", variant="primary", visible=True),
636
+ gr.Button("\u23f9", variant="stop", visible=False),
637
+ ),
638
+ inputs=None,
639
+ outputs=[btn, stop],
640
+ cancels=[gen],
641
+ api_name="stop",
642
+ api_description="Stop the model.",
588
643
  )
589
644
  btn_refresh.click(
590
- fn=refresh,
645
+ fn=_refresh,
591
646
  inputs=[model_name, vocab_fn, token_name],
592
647
  outputs=[
593
648
  vocab_table,
@@ -597,11 +652,14 @@ with gr.Blocks(
597
652
  model_name,
598
653
  vocab_fn,
599
654
  ],
655
+ api_name="refresh_model_list",
656
+ api_description="Refresh the model list.",
600
657
  )
601
658
  token_name.input(
602
- fn=token_name_change_evt,
659
+ fn=_token_name_change_evt,
603
660
  inputs=[token_name, vocab_fn],
604
661
  outputs=[vocab_fn, code, gallery],
662
+ api_visibility="private",
605
663
  )
606
664
  method.input(
607
665
  fn=lambda x, y: gr.Slider(
@@ -614,12 +672,25 @@ with gr.Blocks(
614
672
  ),
615
673
  inputs=[method, temperature],
616
674
  outputs=temperature,
675
+ api_name="select_sampling_method",
676
+ api_description="Select sampling method between 'BFN' and 'ODE'.",
677
+ api_visibility="private",
678
+ )
679
+ lora_tabel.select(
680
+ fn=_select_lora,
681
+ inputs=prompt,
682
+ outputs=prompt,
683
+ api_name="select_lora",
684
+ api_description="Select LoRA model from the model list.",
685
+ api_visibility="private",
617
686
  )
618
- lora_tabel.select(fn=select_lora, inputs=prompt, outputs=prompt)
619
687
  result.change(
620
- fn=lambda x: gr.File(x, label="download", visible=_result_count > 0),
688
+ fn=lambda x: gr.File(x, label="download", visible=_RESULT_COUNT > 0),
621
689
  inputs=btn_download,
622
690
  outputs=btn_download,
691
+ api_name="change_download_state",
692
+ api_description="Hide or show the file downloading item.",
693
+ api_visibility="private",
623
694
  )
624
695
 
625
696
 
@@ -630,9 +701,13 @@ def main() -> None:
630
701
  :return:
631
702
  :rtype: None
632
703
  """
704
+ from rdkit import RDLogger
705
+
706
+ RDLogger.DisableLog("rdApp.*") # type: ignore
633
707
  parser = argparse.ArgumentParser(
634
708
  description="A web-based visualisation tool for ChemBFN method.",
635
- epilog=f"ChemBFN WebUI {__version__}, developed in Hiroshima University by chemists for chemists. "
709
+ epilog=f"ChemBFN WebUI {__version__}, "
710
+ "developed in Hiroshima University by chemists for chemists. "
636
711
  "Visit https://augus1999.github.io/bayesian-flow-network-for-chemistry/ for more details.",
637
712
  formatter_class=argparse.ArgumentDefaultsHelpFormatter,
638
713
  )
@@ -644,8 +719,10 @@ def main() -> None:
644
719
  print(f"This is ChemBFN WebUI version {__version__}")
645
720
  app.launch(
646
721
  share=args.public,
722
+ footer_links=["api"],
647
723
  allowed_paths=[cache_dir.absolute().__str__()],
648
724
  favicon_path=favicon_dir.absolute().__str__(),
725
+ css=".custom_footer {text-align:center;bottom:0;}",
649
726
  )
650
727
 
651
728
 
@@ -0,0 +1 @@
1
+ c1ccccc1