doragera commited on
Commit
05c6778
·
1 Parent(s): 46a04b7

molecule visualizing, requests->httpx, support multiple molecules

Browse files
examples/Generative drug screening.lynxkite.json CHANGED
The diff for this file is too large to render. See raw diff
 
lynxkite-app/web/package-lock.json CHANGED
@@ -18,11 +18,13 @@
18
  "@syncedstore/react": "^0.6.0",
19
  "@types/node": "^22.10.1",
20
  "@xyflow/react": "^12.3.5",
 
21
  "axios": "^1.8.2",
22
  "daisyui": "^4.12.20",
23
  "echarts": "^5.5.1",
24
  "fuse.js": "^7.0.0",
25
  "json-schema-to-typescript": "^15.0.3",
 
26
  "react": "^18.3.1",
27
  "react-dom": "^18.3.1",
28
  "react-markdown": "^9.0.1",
@@ -2366,6 +2368,22 @@
2366
  "d3-zoom": "^3.0.0"
2367
  }
2368
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2369
  "node_modules/abstract-leveldown": {
2370
  "version": "6.2.3",
2371
  "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
@@ -4187,6 +4205,12 @@
4187
  "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==",
4188
  "license": "MIT"
4189
  },
 
 
 
 
 
 
4190
  "node_modules/is-alphabetical": {
4191
  "version": "2.0.1",
4192
  "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
@@ -5465,8 +5489,7 @@
5465
  "version": "0.52.2",
5466
  "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
5467
  "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
5468
- "license": "MIT",
5469
- "peer": true
5470
  },
5471
  "node_modules/ms": {
5472
  "version": "2.1.3",
@@ -5518,6 +5541,15 @@
5518
  "dev": true,
5519
  "license": "MIT"
5520
  },
 
 
 
 
 
 
 
 
 
5521
  "node_modules/no-case": {
5522
  "version": "3.0.4",
5523
  "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@@ -5649,6 +5681,12 @@
5649
  "integrity": "sha512-g4+387DXDKlZzHkP+9FLt8yKj8+/3tOkPv7DVTJGGRm00RkEWgqbFstX1mXJ4M0VDYhUqsTOiISqNOJnhAu3PQ==",
5650
  "license": "MIT"
5651
  },
 
 
 
 
 
 
5652
  "node_modules/parent-module": {
5653
  "version": "1.0.1",
5654
  "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -7107,6 +7145,21 @@
7107
  "browserslist": ">= 4.21.0"
7108
  }
7109
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7110
  "node_modules/uri-js": {
7111
  "version": "4.4.1",
7112
  "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
 
18
  "@syncedstore/react": "^0.6.0",
19
  "@types/node": "^22.10.1",
20
  "@xyflow/react": "^12.3.5",
21
+ "3dmol": "^2.4.2",
22
  "axios": "^1.8.2",
23
  "daisyui": "^4.12.20",
24
  "echarts": "^5.5.1",
25
  "fuse.js": "^7.0.0",
26
  "json-schema-to-typescript": "^15.0.3",
27
+ "monaco-editor": "^0.52.2",
28
  "react": "^18.3.1",
29
  "react-dom": "^18.3.1",
30
  "react-markdown": "^9.0.1",
 
2368
  "d3-zoom": "^3.0.0"
2369
  }
2370
  },
2371
+ "node_modules/3dmol": {
2372
+ "version": "2.4.2",
2373
+ "resolved": "https://registry.npmjs.org/3dmol/-/3dmol-2.4.2.tgz",
2374
+ "integrity": "sha512-7R6uFaF5++s9EpO+R0a3bs6igi5idI+sgrVhtozaZlOGHg6oourYkAlAwYxWRaeBGlNcAd+oEIhbhJYhNxGyZA==",
2375
+ "license": "BSD-3-Clause",
2376
+ "dependencies": {
2377
+ "iobuffer": "^5.3.1",
2378
+ "netcdfjs": "^3.0.0",
2379
+ "pako": "^2.1.0",
2380
+ "upng-js": "^2.1.0"
2381
+ },
2382
+ "engines": {
2383
+ "node": ">=16.16.0",
2384
+ "npm": ">=8.11"
2385
+ }
2386
+ },
2387
  "node_modules/abstract-leveldown": {
2388
  "version": "6.2.3",
2389
  "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
 
4205
  "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==",
4206
  "license": "MIT"
4207
  },
4208
+ "node_modules/iobuffer": {
4209
+ "version": "5.4.0",
4210
+ "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz",
4211
+ "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==",
4212
+ "license": "MIT"
4213
+ },
4214
  "node_modules/is-alphabetical": {
4215
  "version": "2.0.1",
4216
  "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
 
5489
  "version": "0.52.2",
5490
  "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
5491
  "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
5492
+ "license": "MIT"
 
5493
  },
5494
  "node_modules/ms": {
5495
  "version": "2.1.3",
 
5541
  "dev": true,
5542
  "license": "MIT"
5543
  },
5544
+ "node_modules/netcdfjs": {
5545
+ "version": "3.0.0",
5546
+ "resolved": "https://registry.npmjs.org/netcdfjs/-/netcdfjs-3.0.0.tgz",
5547
+ "integrity": "sha512-LOvT8KkC308qtpUkcBPiCMBtii7ZQCN6LxcVheWgyUeZ6DQWcpSRFV9dcVXLj/2eHZ/bre9tV5HTH4Sf93vrFw==",
5548
+ "license": "MIT",
5549
+ "dependencies": {
5550
+ "iobuffer": "^5.3.2"
5551
+ }
5552
+ },
5553
  "node_modules/no-case": {
5554
  "version": "3.0.4",
5555
  "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
 
5681
  "integrity": "sha512-g4+387DXDKlZzHkP+9FLt8yKj8+/3tOkPv7DVTJGGRm00RkEWgqbFstX1mXJ4M0VDYhUqsTOiISqNOJnhAu3PQ==",
5682
  "license": "MIT"
5683
  },
5684
+ "node_modules/pako": {
5685
+ "version": "2.1.0",
5686
+ "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
5687
+ "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
5688
+ "license": "(MIT AND Zlib)"
5689
+ },
5690
  "node_modules/parent-module": {
5691
  "version": "1.0.1",
5692
  "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
 
7145
  "browserslist": ">= 4.21.0"
7146
  }
7147
  },
7148
+ "node_modules/upng-js": {
7149
+ "version": "2.1.0",
7150
+ "resolved": "https://registry.npmjs.org/upng-js/-/upng-js-2.1.0.tgz",
7151
+ "integrity": "sha512-d3xzZzpMP64YkjP5pr8gNyvBt7dLk/uGI67EctzDuVp4lCZyVMo0aJO6l/VDlgbInJYDY6cnClLoBp29eKWI6g==",
7152
+ "license": "MIT",
7153
+ "dependencies": {
7154
+ "pako": "^1.0.5"
7155
+ }
7156
+ },
7157
+ "node_modules/upng-js/node_modules/pako": {
7158
+ "version": "1.0.11",
7159
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
7160
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
7161
+ "license": "(MIT AND Zlib)"
7162
+ },
7163
  "node_modules/uri-js": {
7164
  "version": "4.4.1",
7165
  "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
lynxkite-app/web/package.json CHANGED
@@ -21,11 +21,13 @@
21
  "@syncedstore/react": "^0.6.0",
22
  "@types/node": "^22.10.1",
23
  "@xyflow/react": "^12.3.5",
 
24
  "axios": "^1.8.2",
25
  "daisyui": "^4.12.20",
26
  "echarts": "^5.5.1",
27
  "fuse.js": "^7.0.0",
28
  "json-schema-to-typescript": "^15.0.3",
 
29
  "react": "^18.3.1",
30
  "react-dom": "^18.3.1",
31
  "react-markdown": "^9.0.1",
 
21
  "@syncedstore/react": "^0.6.0",
22
  "@types/node": "^22.10.1",
23
  "@xyflow/react": "^12.3.5",
24
+ "3dmol": "^2.4.2",
25
  "axios": "^1.8.2",
26
  "daisyui": "^4.12.20",
27
  "echarts": "^5.5.1",
28
  "fuse.js": "^7.0.0",
29
  "json-schema-to-typescript": "^15.0.3",
30
+ "monaco-editor": "^0.52.2",
31
  "react": "^18.3.1",
32
  "react-dom": "^18.3.1",
33
  "react-markdown": "^9.0.1",
lynxkite-app/web/src/workspace/Workspace.tsx CHANGED
@@ -36,6 +36,7 @@ import NodeSearch, { type OpsOp, type Catalog, type Catalogs } from "./NodeSearc
36
  import NodeWithGraphCreationView from "./nodes/GraphCreationNode.tsx";
37
  import NodeWithImage from "./nodes/NodeWithImage.tsx";
38
  import NodeWithParams from "./nodes/NodeWithParams";
 
39
  import NodeWithTableView from "./nodes/NodeWithTableView.tsx";
40
  import NodeWithVisualization from "./nodes/NodeWithVisualization.tsx";
41
 
@@ -173,6 +174,7 @@ function LynxKiteFlow() {
173
  image: NodeWithImage,
174
  table_view: NodeWithTableView,
175
  graph_creation_view: NodeWithGraphCreationView,
 
176
  }),
177
  [],
178
  );
 
36
  import NodeWithGraphCreationView from "./nodes/GraphCreationNode.tsx";
37
  import NodeWithImage from "./nodes/NodeWithImage.tsx";
38
  import NodeWithParams from "./nodes/NodeWithParams";
39
+ import NodeWithPy3Dmol from "./nodes/NodeWithPy3DMol.tsx";
40
  import NodeWithTableView from "./nodes/NodeWithTableView.tsx";
41
  import NodeWithVisualization from "./nodes/NodeWithVisualization.tsx";
42
 
 
174
  image: NodeWithImage,
175
  table_view: NodeWithTableView,
176
  graph_creation_view: NodeWithGraphCreationView,
177
+ py3dmol: NodeWithPy3Dmol,
178
  }),
179
  [],
180
  );
lynxkite-app/web/src/workspace/nodes/NodeWithPy3DMol.tsx ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect } from "react";
2
+ import NodeWithParams from "./NodeWithParams";
3
+ const $3Dmol = await import("3dmol");
4
+
5
+ const NodeWithPy3Dmol = (props: any) => {
6
+ const containerRef = React.useRef<HTMLDivElement>(null);
7
+ const viewerRef = React.useRef<any>(null);
8
+
9
+ useEffect(() => {
10
+ const config = props.data?.display?.value;
11
+ if (!config || !containerRef.current) return;
12
+
13
+ try {
14
+ // Initialize viewer only once
15
+ if (!viewerRef.current) {
16
+ viewerRef.current = $3Dmol.createViewer(containerRef.current, {
17
+ backgroundColor: "white",
18
+ });
19
+ }
20
+
21
+ const viewer = viewerRef.current;
22
+
23
+ // Clear previous models
24
+ viewer.clear();
25
+
26
+ // Add new model and style it
27
+ viewer.addModel(config.data, config.format);
28
+ viewer.setStyle({}, { stick: {} });
29
+ viewer.zoomTo();
30
+ viewer.render();
31
+ } catch (error) {
32
+ console.error("Error rendering 3D molecule:", error);
33
+ }
34
+
35
+ const resizeObserver = new ResizeObserver(() => {
36
+ viewerRef.current?.resize();
37
+ });
38
+
39
+ const observed = containerRef.current;
40
+ resizeObserver.observe(observed);
41
+
42
+ return () => {
43
+ resizeObserver.unobserve(observed);
44
+ if (viewerRef.current) {
45
+ viewerRef.current.clear();
46
+ }
47
+ };
48
+ }, [props.data?.display?.value]);
49
+
50
+ const nodeStyle = { display: "flex", flexDirection: "column", height: "100%" };
51
+ const vizStyle = {
52
+ flex: 1,
53
+ minHeight: "300px",
54
+ border: "1px solid #ddd",
55
+ borderRadius: "4px",
56
+ overflow: "hidden",
57
+ position: "relative",
58
+ };
59
+
60
+ return (
61
+ <NodeWithParams nodeStyle={nodeStyle} collapsed {...props}>
62
+ <div style={vizStyle} ref={containerRef} />
63
+ </NodeWithParams>
64
+ );
65
+ };
66
+
67
+ export default NodeWithPy3Dmol;
lynxkite-bio/src/lynxkite_bio/nims.py CHANGED
@@ -3,7 +3,7 @@
3
  from lynxkite_graph_analytics import Bundle
4
  from lynxkite.core import ops
5
  import joblib
6
- import requests
7
  import pandas as pd
8
  import os
9
 
@@ -15,7 +15,7 @@ op = ops.op_registration(ENV)
15
  key = os.getenv("NVCF_RUN_KEY")
16
 
17
 
18
- def query_bionemo_nim(
19
  url: str,
20
  payload: dict,
21
  ):
@@ -26,17 +26,18 @@ def query_bionemo_nim(
26
  }
27
  try:
28
  print(f"Sending request to {url}")
29
- response = requests.post(url, json=payload, headers=headers)
 
30
  print(f"Received response from {url}", response.status_code)
31
  response.raise_for_status()
32
  return response.json()
33
- except requests.exceptions.RequestException as e:
34
  raise ValueError(f"Query failed: {e}")
35
 
36
 
37
  @op("MSA-search")
38
  @mem.cache
39
- def msa_search(
40
  bundle: Bundle,
41
  *,
42
  protein_table: str,
@@ -48,26 +49,34 @@ def msa_search(
48
  databases: str = "Uniref30_2302,colabfold_envdb_202108",
49
  ):
50
  bundle = bundle.copy()
51
- response = query_bionemo_nim(
52
- url="https://health.api.nvidia.com/v1/biology/colabfold/msa-search/predict",
53
- payload={
54
- "sequence": bundle.dfs[protein_table][protein_column].iloc[0],
55
- "e_value": e_value,
56
- "iterations": iterations,
57
- "search_type": search_type,
58
- "output_alignment_formats": [
59
- format for format in output_alignment_formats.split(",")
60
- ],
61
- "databases": [db for db in databases.split(",")],
62
- },
63
- )
64
- bundle.dfs[protein_table]["alignments"] = [response["alignments"]]
 
 
 
 
 
 
 
 
65
  return bundle
66
 
67
 
68
  @op("Query OpenFold2")
69
  @mem.cache
70
- def query_openfold2(
71
  bundle: Bundle,
72
  *,
73
  protein_table: str,
@@ -78,59 +87,57 @@ def query_openfold2(
78
  relax_prediction: bool = False,
79
  ):
80
  bundle = bundle.copy()
81
- protein = bundle.dfs[protein_table][protein_column].iloc[0]
82
- alignments = bundle.dfs[alignment_table][alignment_column].iloc[0]
83
- selected_models = [int(model) for model in selected_models.split(",")]
84
- response = query_bionemo_nim(
85
- url="https://health.api.nvidia.com/v1/biology/openfold/openfold2/predict-structure-from-msa-and-template",
86
- payload={
87
- "sequence": protein,
88
- "alignments": alignments,
89
- "selected_models": selected_models,
90
- "relax_prediction": relax_prediction,
91
- },
92
- )
93
- folded_protein = response["structures_in_ranked_order"].pop(0)["structure"]
94
- bundle.dfs[protein_table]["folded_protein"] = folded_protein
 
 
 
 
 
 
 
 
 
 
 
95
  return bundle
96
 
97
 
98
- @op("View molecules", view="visualization")
99
- def view_molecules(
100
  bundle: Bundle,
101
  *,
102
  molecule_table: str,
103
  molecule_column: str,
104
- color="spectrum",
105
  ):
 
 
106
  return {
107
- "series": [
108
- {
109
- "type": "pie",
110
- "radius": ["40%", "70%"],
111
- "itemStyle": {
112
- "borderRadius": 10,
113
- "borderColor": "#fff",
114
- "borderWidth": 2,
115
- },
116
- "data": [
117
- {"value": 2, "name": "Hydrogen"},
118
- {"value": 1, "name": "Sulfur"},
119
- {"value": 4, "name": "Oxygen"},
120
- ],
121
- }
122
- ]
123
  }
124
 
125
 
126
- @op("Known drug")
127
- def known_drug(*, drug_name: str):
128
- return Bundle()
129
-
130
-
131
  @op("Query GenMol")
132
  @mem.cache
133
- def query_genmol(
134
  bundle: Bundle,
135
  *,
136
  molecule_table: str,
@@ -143,7 +150,7 @@ def query_genmol(
143
  ):
144
  bundle = bundle.copy()
145
 
146
- response = query_bionemo_nim(
147
  url="https://health.api.nvidia.com/v1/biology/nvidia/genmol/generate",
148
  payload={
149
  "smiles": bundle.dfs[molecule_table][molecule_column].iloc[0],
@@ -161,7 +168,7 @@ def query_genmol(
161
 
162
  @op("Query DiffDock")
163
  @mem.cache
164
- def query_diffdock(
165
  proteins: Bundle,
166
  ligands: Bundle,
167
  *,
@@ -174,7 +181,7 @@ def query_diffdock(
174
  time_divisions=20,
175
  num_steps=18,
176
  ):
177
- response = query_bionemo_nim(
178
  url="https://health.api.nvidia.com/v1/biology/mit/diffdock",
179
  payload={
180
  "protein": proteins.dfs[protein_table][protein_column].iloc[0],
@@ -187,17 +194,11 @@ def query_diffdock(
187
  )
188
  bundle = Bundle()
189
  bundle.dfs["diffdock_table"] = pd.DataFrame()
190
- bundle.dfs["diffdock_table"]["protein"] = [response["protein"]] * len(
191
- response["status"]
192
- )
193
- bundle.dfs["diffdock_table"]["ligand"] = [response["ligand"]] * len(
194
- response["status"]
195
- )
196
  bundle.dfs["diffdock_table"]["trajectory"] = response["trajectory"]
197
  bundle.dfs["diffdock_table"]["ligand_positions"] = response["ligand_positions"]
198
- bundle.dfs["diffdock_table"]["position_confidence"] = response[
199
- "position_confidence"
200
- ]
201
  bundle.dfs["diffdock_table"]["status"] = response["status"]
202
 
203
  return bundle
 
3
  from lynxkite_graph_analytics import Bundle
4
  from lynxkite.core import ops
5
  import joblib
6
+ import httpx
7
  import pandas as pd
8
  import os
9
 
 
15
  key = os.getenv("NVCF_RUN_KEY")
16
 
17
 
18
+ async def query_bionemo_nim(
19
  url: str,
20
  payload: dict,
21
  ):
 
26
  }
27
  try:
28
  print(f"Sending request to {url}")
29
+ async with httpx.AsyncClient(timeout=600) as client:
30
+ response = await client.post(url, json=payload, headers=headers)
31
  print(f"Received response from {url}", response.status_code)
32
  response.raise_for_status()
33
  return response.json()
34
+ except httpx.RequestError as e:
35
  raise ValueError(f"Query failed: {e}")
36
 
37
 
38
  @op("MSA-search")
39
  @mem.cache
40
+ async def msa_search(
41
  bundle: Bundle,
42
  *,
43
  protein_table: str,
 
49
  databases: str = "Uniref30_2302,colabfold_envdb_202108",
50
  ):
51
  bundle = bundle.copy()
52
+ bundle.dfs[protein_table]["alignments"] = None
53
+
54
+ formats = [format.strip() for format in output_alignment_formats.split(",")]
55
+ dbs = [db.strip() for db in databases.split(",")]
56
+
57
+ for idx, protein_sequence in enumerate(bundle.dfs[protein_table][protein_column]):
58
+ print(f"Processing protein {idx + 1}/{len(bundle.dfs[protein_table])}")
59
+
60
+ response = await query_bionemo_nim(
61
+ url="https://health.api.nvidia.com/v1/biology/colabfold/msa-search/predict",
62
+ payload={
63
+ "sequence": protein_sequence,
64
+ "e_value": e_value,
65
+ "iterations": iterations,
66
+ "search_type": search_type,
67
+ "output_alignment_formats": formats,
68
+ "databases": dbs,
69
+ },
70
+ )
71
+
72
+ bundle.dfs[protein_table].at[idx, "alignments"] = response["alignments"]
73
+
74
  return bundle
75
 
76
 
77
  @op("Query OpenFold2")
78
  @mem.cache
79
+ async def query_openfold2(
80
  bundle: Bundle,
81
  *,
82
  protein_table: str,
 
87
  relax_prediction: bool = False,
88
  ):
89
  bundle = bundle.copy()
90
+
91
+ bundle.dfs[protein_table]["folded_protein"] = None
92
+ selected_models_list = [int(model) for model in selected_models.split(",")]
93
+
94
+ for idx in range(len(bundle.dfs[protein_table])):
95
+ print(f"Processing protein {idx + 1}/{len(bundle.dfs[protein_table])}")
96
+
97
+ protein = bundle.dfs[protein_table][protein_column].iloc[idx]
98
+ alignments = bundle.dfs[alignment_table][alignment_column].iloc[idx]
99
+
100
+ response = await query_bionemo_nim(
101
+ url="https://health.api.nvidia.com/v1/biology/openfold/openfold2/predict-structure-from-msa-and-template",
102
+ payload={
103
+ "sequence": protein,
104
+ "alignments": alignments,
105
+ "selected_models": selected_models_list,
106
+ "relax_prediction": relax_prediction,
107
+ },
108
+ )
109
+
110
+ folded_protein = response["structures_in_ranked_order"].pop(0)["structure"]
111
+ bundle.dfs[protein_table].at[idx, "folded_protein"] = folded_protein
112
+
113
+ bundle.dfs["openfold"] = pd.DataFrame()
114
+
115
  return bundle
116
 
117
 
118
+ @op("View molecule", view="py3dmol")
119
+ def view_molecule(
120
  bundle: Bundle,
121
  *,
122
  molecule_table: str,
123
  molecule_column: str,
124
+ row_index: int = 0,
125
  ):
126
+ molecule_data = bundle.dfs[molecule_table][molecule_column].iloc[row_index]
127
+
128
  return {
129
+ "data": molecule_data,
130
+ "format": "pdb"
131
+ if molecule_data.startswith("ATOM")
132
+ else "sdf"
133
+ if molecule_data.startswith("CTfile")
134
+ else "smiles",
 
 
 
 
 
 
 
 
 
 
135
  }
136
 
137
 
 
 
 
 
 
138
  @op("Query GenMol")
139
  @mem.cache
140
+ async def query_genmol(
141
  bundle: Bundle,
142
  *,
143
  molecule_table: str,
 
150
  ):
151
  bundle = bundle.copy()
152
 
153
+ response = await query_bionemo_nim(
154
  url="https://health.api.nvidia.com/v1/biology/nvidia/genmol/generate",
155
  payload={
156
  "smiles": bundle.dfs[molecule_table][molecule_column].iloc[0],
 
168
 
169
  @op("Query DiffDock")
170
  @mem.cache
171
+ async def query_diffdock(
172
  proteins: Bundle,
173
  ligands: Bundle,
174
  *,
 
181
  time_divisions=20,
182
  num_steps=18,
183
  ):
184
+ response = await query_bionemo_nim(
185
  url="https://health.api.nvidia.com/v1/biology/mit/diffdock",
186
  payload={
187
  "protein": proteins.dfs[protein_table][protein_column].iloc[0],
 
194
  )
195
  bundle = Bundle()
196
  bundle.dfs["diffdock_table"] = pd.DataFrame()
197
+ bundle.dfs["diffdock_table"]["protein"] = [response["protein"]] * len(response["status"])
198
+ bundle.dfs["diffdock_table"]["ligand"] = [response["ligand"]] * len(response["status"])
 
 
 
 
199
  bundle.dfs["diffdock_table"]["trajectory"] = response["trajectory"]
200
  bundle.dfs["diffdock_table"]["ligand_positions"] = response["ligand_positions"]
201
+ bundle.dfs["diffdock_table"]["position_confidence"] = response["position_confidence"]
 
 
202
  bundle.dfs["diffdock_table"]["status"] = response["status"]
203
 
204
  return bundle
lynxkite-core/src/lynxkite/core/ops.py CHANGED
@@ -180,6 +180,7 @@ class Op(BaseConfig):
180
  "table_view",
181
  "graph_creation_view",
182
  "image",
 
183
  ]:
184
  # If the operation is some kind of visualization, we use the output as the
185
  # value to display by default.
 
180
  "table_view",
181
  "graph_creation_view",
182
  "image",
183
+ "py3dmol",
184
  ]:
185
  # If the operation is some kind of visualization, we use the output as the
186
  # value to display by default.