Spaces:
Running
Running
Merge remote-tracking branch 'origin/main' into feature/image-search-new
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .github/workflows/test.yaml +1 -0
- Dockerfile +6 -2
- biome.json +5 -0
- examples/{AIMO → AIMO.lynxkite.json} +0 -0
- examples/{Airlines demo → Airlines demo.lynxkite.json} +0 -0
- examples/{Bio demo → Bio demo.lynxkite.json} +0 -0
- examples/{BioNeMo demo → BioNeMo demo.lynxkite.json} +0 -0
- examples/{LynxScribe RAG Chatbot.lynxkite.json → Generative drug screening.lynxkite.json} +520 -447
- examples/{Graph RAG → Graph RAG.lynxkite.json} +1 -9
- examples/{Image processing → Image processing.lynxkite.json} +0 -0
- examples/Model definition.lynxkite.json +614 -0
- examples/Model use.lynxkite.json +0 -0
- examples/{NetworkX demo → NetworkX demo.lynxkite.json} +0 -0
- examples/ODE-GNN experiment.lynxkite.json +466 -0
- examples/ODE-GNN.lynxkite.json +796 -0
- examples/{PyTorch demo → PyTorch demo.lynxkite.json} +0 -0
- examples/{RAG chatbot app → RAG chatbot app.lynxkite.json} +0 -0
- examples/Word2vec.lynxkite.json +0 -0
- examples/fake_data.py +16 -0
- examples/requirements.txt +1 -0
- examples/{sql → sql.lynxkite.json} +0 -0
- examples/uploads/plus-one-dataset.parquet +0 -0
- examples/word2vec.py +26 -0
- lynxkite-app/src/lynxkite_app/crdt.py +67 -18
- lynxkite-app/src/lynxkite_app/main.py +14 -6
- lynxkite-app/tests/test_main.py +16 -8
- lynxkite-app/web/eslint.config.js +1 -4
- lynxkite-app/web/index.html +0 -1
- lynxkite-app/web/package-lock.json +55 -0
- lynxkite-app/web/package.json +2 -0
- lynxkite-app/web/playwright.config.ts +2 -1
- lynxkite-app/web/src/Code.tsx +101 -0
- lynxkite-app/web/src/Directory.tsx +105 -108
- lynxkite-app/web/src/apiTypes.ts +4 -1
- lynxkite-app/web/src/code-theme.ts +38 -0
- lynxkite-app/web/src/index.css +39 -3
- lynxkite-app/web/src/main.tsx +2 -0
- lynxkite-app/web/src/workspace/NodeSearch.tsx +2 -7
- lynxkite-app/web/src/workspace/Workspace.tsx +20 -42
- lynxkite-app/web/src/workspace/nodes/GraphCreationNode.tsx +4 -16
- lynxkite-app/web/src/workspace/nodes/LynxKiteNode.tsx +6 -19
- lynxkite-app/web/src/workspace/nodes/NodeGroupParameter.tsx +5 -6
- lynxkite-app/web/src/workspace/nodes/NodeParameter.tsx +199 -19
- lynxkite-app/web/src/workspace/nodes/NodeWithImage.tsx +1 -3
- lynxkite-app/web/src/workspace/nodes/NodeWithParams.tsx +6 -11
- lynxkite-app/web/src/workspace/nodes/NodeWithTableView.tsx +22 -18
- lynxkite-app/web/src/workspace/nodes/NodeWithVisualization.tsx +4 -0
- lynxkite-app/web/tests/directory.spec.ts +0 -12
- lynxkite-app/web/tests/errors.spec.ts +1 -3
- lynxkite-app/web/tests/graph_creation.spec.ts +6 -15
.github/workflows/test.yaml
CHANGED
@@ -81,6 +81,7 @@ jobs:
|
|
81 |
- name: Run Playwright tests
|
82 |
run: |
|
83 |
cd lynxkite-app/web
|
|
|
84 |
npm run test
|
85 |
|
86 |
- uses: actions/upload-artifact@v4
|
|
|
81 |
- name: Run Playwright tests
|
82 |
run: |
|
83 |
cd lynxkite-app/web
|
84 |
+
npm run build
|
85 |
npm run test
|
86 |
|
87 |
- uses: actions/upload-artifact@v4
|
Dockerfile
CHANGED
@@ -5,12 +5,16 @@ USER node
|
|
5 |
ENV HOME=/home/node PATH=/home/node/.local/bin:$PATH
|
6 |
WORKDIR $HOME/app
|
7 |
COPY --chown=node . $HOME/app
|
8 |
-
|
|
|
|
|
9 |
-e lynxkite-core \
|
10 |
-e lynxkite-app \
|
11 |
-e lynxkite-graph-analytics \
|
12 |
-e lynxkite-bio \
|
13 |
-
-e lynxkite-
|
|
|
|
|
14 |
WORKDIR $HOME/app/examples
|
15 |
ENV PORT=7860
|
16 |
CMD ["uv", "run", "lynxkite"]
|
|
|
5 |
ENV HOME=/home/node PATH=/home/node/.local/bin:$PATH
|
6 |
WORKDIR $HOME/app
|
7 |
COPY --chown=node . $HOME/app
|
8 |
+
ENV GIT_SSH_COMMAND="ssh -i /run/secrets/LYNXSCRIBE_DEPLOY_KEY -o StrictHostKeyChecking=no"
|
9 |
+
RUN --mount=type=secret,id=LYNXSCRIBE_DEPLOY_KEY,mode=0444,required=true \
|
10 |
+
uv venv && uv pip install \
|
11 |
-e lynxkite-core \
|
12 |
-e lynxkite-app \
|
13 |
-e lynxkite-graph-analytics \
|
14 |
-e lynxkite-bio \
|
15 |
+
-e lynxkite-lynxscribe \
|
16 |
+
-e lynxkite-pillow-example \
|
17 |
+
chromadb openai
|
18 |
WORKDIR $HOME/app/examples
|
19 |
ENV PORT=7860
|
20 |
CMD ["uv", "run", "lynxkite"]
|
biome.json
CHANGED
@@ -1,6 +1,10 @@
|
|
1 |
{
|
|
|
|
|
|
|
2 |
"formatter": {
|
3 |
"ignore": ["**/node_modules/**", "**/dist/**"],
|
|
|
4 |
"indentStyle": "space"
|
5 |
},
|
6 |
"linter": {
|
@@ -20,6 +24,7 @@
|
|
20 |
"useKeyWithClickEvents": "off",
|
21 |
"useValidAnchor": "off",
|
22 |
"useButtonType": "off",
|
|
|
23 |
"noNoninteractiveTabindex": "off"
|
24 |
}
|
25 |
}
|
|
|
1 |
{
|
2 |
+
"files": {
|
3 |
+
"ignore": ["**/*.lynxkite.json"]
|
4 |
+
},
|
5 |
"formatter": {
|
6 |
"ignore": ["**/node_modules/**", "**/dist/**"],
|
7 |
+
"lineWidth": 100,
|
8 |
"indentStyle": "space"
|
9 |
},
|
10 |
"linter": {
|
|
|
24 |
"useKeyWithClickEvents": "off",
|
25 |
"useValidAnchor": "off",
|
26 |
"useButtonType": "off",
|
27 |
+
"noAutofocus": "off",
|
28 |
"noNoninteractiveTabindex": "off"
|
29 |
}
|
30 |
}
|
examples/{AIMO → AIMO.lynxkite.json}
RENAMED
File without changes
|
examples/{Airlines demo → Airlines demo.lynxkite.json}
RENAMED
File without changes
|
examples/{Bio demo → Bio demo.lynxkite.json}
RENAMED
File without changes
|
examples/{BioNeMo demo → BioNeMo demo.lynxkite.json}
RENAMED
File without changes
|
examples/{LynxScribe RAG Chatbot.lynxkite.json → Generative drug screening.lynxkite.json}
RENAMED
@@ -1,77 +1,56 @@
|
|
1 |
{
|
2 |
"edges": [
|
3 |
{
|
4 |
-
"id": "
|
5 |
-
"source": "
|
6 |
"sourceHandle": "output",
|
7 |
-
"target": "
|
8 |
-
"targetHandle": "
|
9 |
},
|
10 |
{
|
11 |
-
"id": "
|
12 |
-
"source": "
|
13 |
"sourceHandle": "output",
|
14 |
-
"target": "
|
15 |
-
"targetHandle": "
|
16 |
},
|
17 |
{
|
18 |
-
"id": "
|
19 |
-
"source": "
|
20 |
"sourceHandle": "output",
|
21 |
-
"target": "
|
22 |
-
"targetHandle": "
|
23 |
},
|
24 |
{
|
25 |
-
"id": "
|
26 |
-
"source": "
|
27 |
"sourceHandle": "output",
|
28 |
-
"target": "
|
29 |
-
"targetHandle": "
|
30 |
},
|
31 |
{
|
32 |
-
"id": "
|
33 |
-
"source": "
|
34 |
"sourceHandle": "output",
|
35 |
-
"target": "
|
36 |
-
"targetHandle": "
|
37 |
},
|
38 |
{
|
39 |
-
"id": "
|
40 |
-
"source": "
|
41 |
"sourceHandle": "output",
|
42 |
-
"target": "
|
43 |
-
"targetHandle": "
|
44 |
},
|
45 |
{
|
46 |
-
"id": "
|
47 |
-
"source": "
|
48 |
"sourceHandle": "output",
|
49 |
-
"target": "
|
50 |
-
"targetHandle": "
|
51 |
-
},
|
52 |
-
{
|
53 |
-
"id": "Cloud-sourced File Listing 1 LynxScribe Text RAG Loader 1",
|
54 |
-
"source": "Cloud-sourced File Listing 1",
|
55 |
-
"sourceHandle": "output",
|
56 |
-
"target": "LynxScribe Text RAG Loader 1",
|
57 |
-
"targetHandle": "file_urls"
|
58 |
-
},
|
59 |
-
{
|
60 |
-
"id": "LynxScribe Text RAG Loader 1 LynxScribe RAG Graph Chatbot Builder 1",
|
61 |
-
"source": "LynxScribe Text RAG Loader 1",
|
62 |
-
"sourceHandle": "output",
|
63 |
-
"target": "LynxScribe RAG Graph Chatbot Builder 1",
|
64 |
-
"targetHandle": "rag_graph"
|
65 |
-
},
|
66 |
-
{
|
67 |
-
"id": "LynxScribe RAG Graph Chatbot Builder 1 LynxScribe RAG Graph Chatbot Backend 1",
|
68 |
-
"source": "LynxScribe RAG Graph Chatbot Builder 1",
|
69 |
-
"sourceHandle": "output",
|
70 |
-
"target": "LynxScribe RAG Graph Chatbot Backend 1",
|
71 |
-
"targetHandle": "knowledge_base"
|
72 |
}
|
73 |
],
|
74 |
-
"env": "
|
75 |
"nodes": [
|
76 |
{
|
77 |
"data": {
|
@@ -81,7 +60,7 @@
|
|
81 |
"error": null,
|
82 |
"meta": {
|
83 |
"inputs": {},
|
84 |
-
"name": "
|
85 |
"outputs": {
|
86 |
"output": {
|
87 |
"name": "output",
|
@@ -92,9 +71,62 @@
|
|
92 |
}
|
93 |
},
|
94 |
"params": {
|
95 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
"default": null,
|
97 |
-
"name": "
|
98 |
"type": {
|
99 |
"type": "<class 'str'>"
|
100 |
}
|
@@ -103,635 +135,636 @@
|
|
103 |
"type": "basic"
|
104 |
},
|
105 |
"params": {
|
106 |
-
"
|
|
|
|
|
|
|
|
|
107 |
},
|
108 |
"status": "done",
|
109 |
-
"title": "
|
110 |
},
|
111 |
"dragHandle": ".bg-primary",
|
112 |
-
"height":
|
113 |
-
"id": "
|
114 |
-
"parentId": null,
|
115 |
"position": {
|
116 |
-
"x": -
|
117 |
-
"y":
|
118 |
},
|
119 |
"type": "basic",
|
120 |
-
"width":
|
121 |
-
},
|
122 |
-
{
|
123 |
-
"data": {
|
124 |
-
"display": {
|
125 |
-
"dataframes": {
|
126 |
-
"df": {
|
127 |
-
"columns": ["answer"],
|
128 |
-
"data": [
|
129 |
-
[
|
130 |
-
"Lynx Analytics has two notable professionals named G\u00e1bor. Could you please specify which G\u00e1bor you are inquiring about?\n\n- **G\u00e1bor Benedek**: Chief Innovation Officer & Co-founder at Lynx Analytics. He specializes in economic and business simulations, social network analysis, data mining, and predictive analytics. He has an academic background as a former Associate Professor at Corvinus University of Budapest and has founded several data-related companies.\n\n- **G\u00e1bor Kriv\u00e1chy**: Country Manager at Lynx Analytics in Hungary. He is an experienced technology executive with a background in system implementation, integration, and project management, particularly in SAP implementations.\n\nLet me know which G\u00e1bor's details you would like to learn more about!"
|
131 |
-
]
|
132 |
-
]
|
133 |
-
}
|
134 |
-
}
|
135 |
-
},
|
136 |
-
"error": null,
|
137 |
-
"meta": {
|
138 |
-
"inputs": {
|
139 |
-
"input": {
|
140 |
-
"name": "input",
|
141 |
-
"position": "left",
|
142 |
-
"type": {
|
143 |
-
"type": "<class 'inspect._empty'>"
|
144 |
-
}
|
145 |
-
}
|
146 |
-
},
|
147 |
-
"name": "View",
|
148 |
-
"outputs": {},
|
149 |
-
"params": {},
|
150 |
-
"type": "table_view"
|
151 |
-
},
|
152 |
-
"params": {},
|
153 |
-
"status": "done",
|
154 |
-
"title": "View"
|
155 |
-
},
|
156 |
-
"dragHandle": ".bg-primary",
|
157 |
-
"height": 950.0,
|
158 |
-
"id": "View 1",
|
159 |
-
"parentId": null,
|
160 |
-
"position": {
|
161 |
-
"x": -754.9225960536905,
|
162 |
-
"y": -643.161064357758
|
163 |
-
},
|
164 |
-
"type": "table_view",
|
165 |
-
"width": 1256.0
|
166 |
},
|
167 |
{
|
168 |
"data": {
|
|
|
|
|
169 |
"display": null,
|
170 |
"error": null,
|
171 |
"meta": {
|
172 |
"inputs": {},
|
173 |
-
"name": "
|
174 |
"outputs": {
|
175 |
"output": {
|
176 |
"name": "output",
|
177 |
-
"position": "
|
178 |
"type": {
|
179 |
"type": "None"
|
180 |
}
|
181 |
}
|
182 |
},
|
183 |
"params": {
|
184 |
-
"
|
185 |
-
"default":
|
186 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
"type": {
|
188 |
-
"type": "<class '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
}
|
190 |
}
|
191 |
},
|
192 |
"type": "basic"
|
193 |
},
|
194 |
"params": {
|
195 |
-
"
|
|
|
|
|
|
|
|
|
196 |
},
|
197 |
"status": "done",
|
198 |
-
"title": "
|
199 |
},
|
200 |
"dragHandle": ".bg-primary",
|
201 |
-
"height":
|
202 |
-
"id": "
|
203 |
-
"parentId": null,
|
204 |
"position": {
|
205 |
-
"x":
|
206 |
-
"y":
|
207 |
},
|
208 |
"type": "basic",
|
209 |
-
"width":
|
210 |
},
|
211 |
{
|
212 |
"data": {
|
213 |
-
"__execution_delay": null,
|
214 |
-
"collapsed": false,
|
215 |
"display": null,
|
216 |
"error": null,
|
217 |
"meta": {
|
218 |
"inputs": {
|
219 |
-
"
|
220 |
-
"name": "
|
221 |
-
"position": "
|
222 |
-
"type": {
|
223 |
-
"type": "<class 'inspect._empty'>"
|
224 |
-
}
|
225 |
-
}
|
226 |
-
},
|
227 |
-
"name": "Chat processor",
|
228 |
-
"outputs": {
|
229 |
-
"output": {
|
230 |
-
"name": "output",
|
231 |
-
"position": "top",
|
232 |
"type": {
|
233 |
-
"type": "
|
234 |
}
|
235 |
}
|
236 |
},
|
237 |
-
"
|
238 |
-
"type": "basic"
|
239 |
-
},
|
240 |
-
"params": {},
|
241 |
-
"status": "done",
|
242 |
-
"title": "Chat processor"
|
243 |
-
},
|
244 |
-
"dragHandle": ".bg-primary",
|
245 |
-
"height": 89.0,
|
246 |
-
"id": "Chat processor 1",
|
247 |
-
"parentId": null,
|
248 |
-
"position": {
|
249 |
-
"x": -1527.1027075359414,
|
250 |
-
"y": 605.2129408898476
|
251 |
-
},
|
252 |
-
"type": "basic",
|
253 |
-
"width": 416.0
|
254 |
-
},
|
255 |
-
{
|
256 |
-
"data": {
|
257 |
-
"__execution_delay": 0.0,
|
258 |
-
"collapsed": null,
|
259 |
-
"display": null,
|
260 |
-
"error": null,
|
261 |
-
"meta": {
|
262 |
-
"inputs": {},
|
263 |
-
"name": "Mask",
|
264 |
"outputs": {
|
265 |
"output": {
|
266 |
"name": "output",
|
267 |
-
"position": "
|
268 |
"type": {
|
269 |
"type": "None"
|
270 |
}
|
271 |
}
|
272 |
},
|
273 |
"params": {
|
274 |
-
"
|
275 |
-
"default":
|
276 |
-
"name": "
|
277 |
"type": {
|
278 |
"type": "<class 'str'>"
|
279 |
}
|
280 |
},
|
281 |
-
"
|
282 |
-
"default":
|
283 |
-
"name": "
|
284 |
"type": {
|
285 |
"type": "<class 'str'>"
|
286 |
}
|
287 |
},
|
288 |
-
"
|
289 |
-
"default":
|
290 |
-
"name": "
|
291 |
"type": {
|
292 |
-
"type": "<class '
|
293 |
}
|
294 |
},
|
295 |
-
"
|
296 |
-
"default":
|
297 |
-
"name": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
298 |
"type": {
|
299 |
"type": "<class 'str'>"
|
300 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
301 |
}
|
302 |
},
|
|
|
|
|
|
|
|
|
303 |
"type": "basic"
|
304 |
},
|
305 |
"params": {
|
306 |
-
"
|
307 |
-
"
|
308 |
-
"
|
309 |
-
"
|
|
|
|
|
|
|
310 |
},
|
311 |
"status": "done",
|
312 |
-
"title": "
|
313 |
},
|
314 |
"dragHandle": ".bg-primary",
|
315 |
-
"height":
|
316 |
-
"id": "
|
317 |
-
"parentId": null,
|
318 |
"position": {
|
319 |
-
"x":
|
320 |
-
"y":
|
321 |
},
|
322 |
"type": "basic",
|
323 |
-
"width":
|
324 |
},
|
325 |
{
|
326 |
"data": {
|
327 |
-
"__execution_delay": 0.0,
|
328 |
"collapsed": null,
|
329 |
"display": null,
|
330 |
"error": null,
|
331 |
"meta": {
|
332 |
-
"inputs": {
|
333 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
334 |
"outputs": {
|
335 |
"output": {
|
336 |
"name": "output",
|
337 |
-
"position": "
|
338 |
"type": {
|
339 |
"type": "None"
|
340 |
}
|
341 |
}
|
342 |
},
|
343 |
"params": {
|
344 |
-
"
|
345 |
-
"default": "",
|
346 |
-
"name": "
|
347 |
"type": {
|
348 |
"type": "<class 'str'>"
|
349 |
}
|
350 |
},
|
351 |
-
"
|
352 |
-
"default":
|
353 |
-
"name": "
|
354 |
"type": {
|
355 |
-
"type": "<class '
|
356 |
}
|
357 |
},
|
358 |
-
"
|
359 |
-
"default":
|
360 |
-
"name": "
|
361 |
"type": {
|
362 |
-
"type": "<class '
|
363 |
}
|
364 |
},
|
365 |
-
"
|
366 |
-
"default":
|
367 |
-
|
|
|
|
|
|
|
368 |
"type": {
|
369 |
-
"type": "
|
370 |
}
|
371 |
-
}
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
"params": {
|
376 |
-
"exceptions": "",
|
377 |
-
"mask_pattern": "masked_credit_card_number_{}",
|
378 |
-
"name": "credit_card",
|
379 |
-
"regex": "((?:(?:\\\\d{4}[- ]?){3}\\\\d{4}|\\\\d{15,16}))(?![\\\\d])"
|
380 |
-
},
|
381 |
-
"status": "done",
|
382 |
-
"title": "Mask"
|
383 |
-
},
|
384 |
-
"dragHandle": ".bg-primary",
|
385 |
-
"height": 358.0,
|
386 |
-
"id": "Mask 2",
|
387 |
-
"parentId": null,
|
388 |
-
"position": {
|
389 |
-
"x": -983.2612912523697,
|
390 |
-
"y": 731.5859900002104
|
391 |
-
},
|
392 |
-
"type": "basic",
|
393 |
-
"width": 315.0
|
394 |
-
},
|
395 |
-
{
|
396 |
-
"data": {
|
397 |
-
"__execution_delay": 0.0,
|
398 |
-
"collapsed": false,
|
399 |
-
"display": null,
|
400 |
-
"error": null,
|
401 |
-
"meta": {
|
402 |
-
"inputs": {
|
403 |
-
"chat_api": {
|
404 |
-
"name": "chat_api",
|
405 |
-
"position": "bottom",
|
406 |
"type": {
|
407 |
-
"type": "<class '
|
408 |
}
|
409 |
},
|
410 |
-
"
|
411 |
-
"
|
412 |
-
"
|
413 |
"type": {
|
414 |
-
"type": "<class '
|
415 |
}
|
416 |
-
}
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
"output": {
|
421 |
-
"name": "output",
|
422 |
-
"position": "right",
|
423 |
"type": {
|
424 |
-
"
|
|
|
|
|
|
|
425 |
}
|
426 |
}
|
427 |
},
|
428 |
-
"
|
429 |
-
"
|
430 |
-
|
431 |
-
"name": "show_details",
|
432 |
-
"type": {
|
433 |
-
"type": "<class 'bool'>"
|
434 |
-
}
|
435 |
-
}
|
436 |
},
|
437 |
"type": "basic"
|
438 |
},
|
439 |
-
"params": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
440 |
"status": "done",
|
441 |
-
"title": "
|
442 |
},
|
443 |
"dragHandle": ".bg-primary",
|
444 |
-
"height":
|
445 |
-
"id": "
|
446 |
-
"parentId": null,
|
447 |
"position": {
|
448 |
-
"x": -
|
449 |
-
"y":
|
450 |
},
|
451 |
"type": "basic",
|
452 |
-
"width":
|
453 |
},
|
454 |
{
|
455 |
"data": {
|
456 |
-
"__execution_delay": 0.0,
|
457 |
-
"collapsed": null,
|
458 |
"display": null,
|
459 |
"error": null,
|
460 |
"meta": {
|
461 |
"inputs": {
|
462 |
-
"
|
463 |
-
"name": "
|
464 |
-
"position": "
|
465 |
"type": {
|
466 |
-
"type": "<class '
|
467 |
}
|
468 |
},
|
469 |
-
"
|
470 |
-
"name": "
|
471 |
-
"position": "
|
472 |
"type": {
|
473 |
-
"type": "<class '
|
474 |
}
|
475 |
}
|
476 |
},
|
477 |
-
"name": "
|
478 |
"outputs": {
|
479 |
"output": {
|
480 |
"name": "output",
|
481 |
-
"position": "
|
482 |
"type": {
|
483 |
"type": "None"
|
484 |
}
|
485 |
}
|
486 |
},
|
487 |
"params": {
|
488 |
-
"
|
489 |
-
"default":
|
490 |
-
"name": "
|
491 |
-
"type": {
|
492 |
-
"type": "<class 'str'>"
|
493 |
-
}
|
494 |
-
},
|
495 |
-
"llm_model_name": {
|
496 |
-
"default": "gpt-4o",
|
497 |
-
"name": "llm_model_name",
|
498 |
"type": {
|
499 |
"type": "<class 'str'>"
|
500 |
}
|
501 |
},
|
502 |
-
"
|
503 |
-
"default":
|
504 |
-
"name": "
|
505 |
"type": {
|
506 |
"type": "<class 'str'>"
|
507 |
}
|
508 |
},
|
509 |
-
"
|
510 |
-
"default":
|
511 |
-
"name": "
|
512 |
"type": {
|
513 |
-
"type": "<class '
|
514 |
}
|
515 |
},
|
516 |
-
"
|
517 |
-
"default":
|
518 |
-
"name": "
|
519 |
"type": {
|
520 |
"type": "<class 'int'>"
|
521 |
}
|
522 |
},
|
523 |
-
"
|
524 |
-
"default":
|
525 |
-
"name": "
|
526 |
"type": {
|
527 |
-
"type": "<class '
|
528 |
}
|
529 |
},
|
530 |
-
"
|
531 |
-
"default":
|
532 |
-
"name": "
|
533 |
"type": {
|
534 |
-
"type": "<class '
|
535 |
}
|
536 |
},
|
537 |
-
"
|
538 |
-
"default":
|
539 |
-
"name": "
|
540 |
"type": {
|
541 |
-
"type": "<class '
|
542 |
}
|
543 |
}
|
544 |
},
|
545 |
"position": {
|
546 |
-
"x":
|
547 |
-
"y":
|
548 |
},
|
549 |
"type": "basic"
|
550 |
},
|
551 |
"params": {
|
552 |
-
"
|
553 |
-
"
|
554 |
-
"
|
555 |
-
"
|
556 |
-
"
|
557 |
-
"
|
558 |
-
"
|
559 |
-
"retriever_strict_limits": true
|
560 |
},
|
561 |
"status": "done",
|
562 |
-
"title": "
|
563 |
},
|
564 |
"dragHandle": ".bg-primary",
|
565 |
-
"height":
|
566 |
-
"id": "
|
567 |
"position": {
|
568 |
-
"x":
|
569 |
-
"y":
|
570 |
},
|
571 |
"type": "basic",
|
572 |
-
"width":
|
573 |
},
|
574 |
{
|
575 |
"data": {
|
576 |
-
"
|
577 |
-
|
578 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
579 |
"error": null,
|
580 |
"meta": {
|
581 |
"inputs": {
|
582 |
-
"
|
583 |
-
"name": "
|
584 |
"position": "left",
|
585 |
"type": {
|
586 |
-
"type": "<class '
|
587 |
-
}
|
588 |
-
}
|
589 |
-
},
|
590 |
-
"name": "LynxScribe Text RAG Loader",
|
591 |
-
"outputs": {
|
592 |
-
"output": {
|
593 |
-
"name": "output",
|
594 |
-
"position": "right",
|
595 |
-
"type": {
|
596 |
-
"type": "None"
|
597 |
}
|
598 |
}
|
599 |
},
|
|
|
|
|
600 |
"params": {
|
601 |
-
"
|
602 |
-
"default": "
|
603 |
-
"name": "
|
604 |
-
"type": {
|
605 |
-
"enum": ["V1", "V2"]
|
606 |
-
}
|
607 |
-
},
|
608 |
-
"text_embedder_interface": {
|
609 |
-
"default": "openai",
|
610 |
-
"name": "text_embedder_interface",
|
611 |
"type": {
|
612 |
"type": "<class 'str'>"
|
613 |
}
|
614 |
},
|
615 |
-
"
|
616 |
-
"default":
|
617 |
-
"name": "
|
618 |
-
"type": {
|
619 |
-
"type": "<class 'str'>"
|
620 |
-
}
|
621 |
-
},
|
622 |
-
"vdb_collection_name": {
|
623 |
-
"default": "lynx",
|
624 |
-
"name": "vdb_collection_name",
|
625 |
"type": {
|
626 |
"type": "<class 'str'>"
|
627 |
}
|
628 |
},
|
629 |
-
"
|
630 |
-
"default":
|
631 |
-
"name": "
|
632 |
-
"type": {
|
633 |
-
"type": "<class 'int'>"
|
634 |
-
}
|
635 |
-
},
|
636 |
-
"vdb_provider_name": {
|
637 |
-
"default": "faiss",
|
638 |
-
"name": "vdb_provider_name",
|
639 |
"type": {
|
640 |
"type": "<class 'str'>"
|
641 |
}
|
642 |
}
|
643 |
},
|
644 |
"position": {
|
645 |
-
"x":
|
646 |
-
"y":
|
647 |
},
|
648 |
-
"type": "
|
649 |
},
|
650 |
"params": {
|
651 |
-
"
|
652 |
-
"
|
653 |
-
"
|
654 |
-
"vdb_collection_name": "lynx",
|
655 |
-
"vdb_num_dimensions": "1536",
|
656 |
-
"vdb_provider_name": "faiss"
|
657 |
},
|
658 |
"status": "done",
|
659 |
-
"title": "
|
660 |
},
|
661 |
"dragHandle": ".bg-primary",
|
662 |
-
"height":
|
663 |
-
"id": "
|
664 |
"position": {
|
665 |
-
"x":
|
666 |
-
"y":
|
667 |
},
|
668 |
-
"type": "
|
669 |
-
"width":
|
670 |
},
|
671 |
{
|
672 |
"data": {
|
673 |
-
"
|
674 |
-
|
675 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
676 |
"error": null,
|
677 |
"meta": {
|
678 |
-
"inputs": {
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
"name": "output",
|
683 |
-
"position": "right",
|
684 |
"type": {
|
685 |
-
"type": "
|
686 |
}
|
687 |
}
|
688 |
},
|
|
|
|
|
689 |
"params": {
|
690 |
-
"
|
691 |
-
"default": "
|
692 |
-
"name": "
|
693 |
"type": {
|
694 |
"type": "<class 'str'>"
|
695 |
}
|
696 |
},
|
697 |
-
"
|
698 |
-
"default":
|
699 |
-
"name": "
|
700 |
"type": {
|
701 |
-
"
|
702 |
}
|
703 |
},
|
704 |
-
"
|
705 |
-
"default":
|
706 |
-
"name": "
|
707 |
"type": {
|
708 |
"type": "<class 'str'>"
|
709 |
}
|
710 |
}
|
711 |
},
|
712 |
"position": {
|
713 |
-
"x":
|
714 |
-
"y":
|
715 |
},
|
716 |
-
"type": "
|
717 |
},
|
718 |
"params": {
|
719 |
-
"
|
720 |
-
"
|
721 |
-
"
|
722 |
},
|
723 |
"status": "done",
|
724 |
-
"title": "
|
725 |
},
|
726 |
"dragHandle": ".bg-primary",
|
727 |
-
"height":
|
728 |
-
"id": "
|
729 |
"position": {
|
730 |
-
"x":
|
731 |
-
"y":
|
732 |
},
|
733 |
-
"type": "
|
734 |
-
"width":
|
735 |
},
|
736 |
{
|
737 |
"data": {
|
@@ -739,62 +772,102 @@
|
|
739 |
"error": null,
|
740 |
"meta": {
|
741 |
"inputs": {
|
742 |
-
"
|
743 |
-
"name": "
|
744 |
"position": "left",
|
745 |
"type": {
|
746 |
-
"type": "<class '
|
747 |
}
|
748 |
}
|
749 |
},
|
750 |
-
"name": "
|
751 |
"outputs": {
|
752 |
"output": {
|
753 |
"name": "output",
|
754 |
-
"position": "
|
755 |
"type": {
|
756 |
"type": "None"
|
757 |
}
|
758 |
}
|
759 |
},
|
760 |
"params": {
|
761 |
-
"
|
762 |
-
"default":
|
763 |
-
"name": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
764 |
"type": {
|
765 |
"type": "<class 'str'>"
|
766 |
}
|
767 |
},
|
768 |
-
"
|
769 |
-
"default":
|
770 |
-
"name": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
771 |
"type": {
|
772 |
"type": "<class 'str'>"
|
773 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
774 |
}
|
775 |
},
|
776 |
"position": {
|
777 |
-
"x":
|
778 |
-
"y":
|
779 |
},
|
780 |
"type": "basic"
|
781 |
},
|
782 |
"params": {
|
783 |
-
"
|
784 |
-
"
|
|
|
|
|
|
|
|
|
|
|
785 |
},
|
786 |
"status": "done",
|
787 |
-
"title": "
|
788 |
},
|
789 |
"dragHandle": ".bg-primary",
|
790 |
-
"height":
|
791 |
-
"id": "
|
792 |
"position": {
|
793 |
-
"x":
|
794 |
-
"y":
|
795 |
},
|
796 |
"type": "basic",
|
797 |
-
"width":
|
798 |
}
|
799 |
]
|
800 |
}
|
|
|
1 |
{
|
2 |
"edges": [
|
3 |
{
|
4 |
+
"id": "Import file 2 Query GenMol 1",
|
5 |
+
"source": "Import file 2",
|
6 |
"sourceHandle": "output",
|
7 |
+
"target": "Query GenMol 1",
|
8 |
+
"targetHandle": "bundle"
|
9 |
},
|
10 |
{
|
11 |
+
"id": "Import file 1 MSA-search 1",
|
12 |
+
"source": "Import file 1",
|
13 |
"sourceHandle": "output",
|
14 |
+
"target": "MSA-search 1",
|
15 |
+
"targetHandle": "bundle"
|
16 |
},
|
17 |
{
|
18 |
+
"id": "Query GenMol 1 Query DiffDock 1",
|
19 |
+
"source": "Query GenMol 1",
|
20 |
"sourceHandle": "output",
|
21 |
+
"target": "Query DiffDock 1",
|
22 |
+
"targetHandle": "ligands"
|
23 |
},
|
24 |
{
|
25 |
+
"id": "Query DiffDock 1 View molecules 1",
|
26 |
+
"source": "Query DiffDock 1",
|
27 |
"sourceHandle": "output",
|
28 |
+
"target": "View molecules 1",
|
29 |
+
"targetHandle": "bundle"
|
30 |
},
|
31 |
{
|
32 |
+
"id": "MSA-search 1 Query OpenFold2 1",
|
33 |
+
"source": "MSA-search 1",
|
34 |
"sourceHandle": "output",
|
35 |
+
"target": "Query OpenFold2 1",
|
36 |
+
"targetHandle": "bundle"
|
37 |
},
|
38 |
{
|
39 |
+
"id": "Query OpenFold2 1 View molecules 3",
|
40 |
+
"source": "Query OpenFold2 1",
|
41 |
"sourceHandle": "output",
|
42 |
+
"target": "View molecules 3",
|
43 |
+
"targetHandle": "bundle"
|
44 |
},
|
45 |
{
|
46 |
+
"id": "Query OpenFold2 1 Query DiffDock 1",
|
47 |
+
"source": "Query OpenFold2 1",
|
48 |
"sourceHandle": "output",
|
49 |
+
"target": "Query DiffDock 1",
|
50 |
+
"targetHandle": "proteins"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
}
|
52 |
],
|
53 |
+
"env": "LynxKite Graph Analytics",
|
54 |
"nodes": [
|
55 |
{
|
56 |
"data": {
|
|
|
60 |
"error": null,
|
61 |
"meta": {
|
62 |
"inputs": {},
|
63 |
+
"name": "Import file",
|
64 |
"outputs": {
|
65 |
"output": {
|
66 |
"name": "output",
|
|
|
71 |
}
|
72 |
},
|
73 |
"params": {
|
74 |
+
"file_format": {
|
75 |
+
"default": "csv",
|
76 |
+
"groups": {
|
77 |
+
"csv": [
|
78 |
+
{
|
79 |
+
"default": "<from file>",
|
80 |
+
"name": "columns",
|
81 |
+
"type": {
|
82 |
+
"type": "<class 'str'>"
|
83 |
+
}
|
84 |
+
},
|
85 |
+
{
|
86 |
+
"default": "<auto>",
|
87 |
+
"name": "separator",
|
88 |
+
"type": {
|
89 |
+
"type": "<class 'str'>"
|
90 |
+
}
|
91 |
+
}
|
92 |
+
],
|
93 |
+
"excel": [
|
94 |
+
{
|
95 |
+
"default": "Sheet1",
|
96 |
+
"name": "sheet_name",
|
97 |
+
"type": {
|
98 |
+
"type": "<class 'str'>"
|
99 |
+
}
|
100 |
+
}
|
101 |
+
],
|
102 |
+
"json": [],
|
103 |
+
"parquet": []
|
104 |
+
},
|
105 |
+
"name": "file_format",
|
106 |
+
"selector": {
|
107 |
+
"default": "csv",
|
108 |
+
"name": "file_format",
|
109 |
+
"type": {
|
110 |
+
"enum": [
|
111 |
+
"csv",
|
112 |
+
"parquet",
|
113 |
+
"json",
|
114 |
+
"excel"
|
115 |
+
]
|
116 |
+
}
|
117 |
+
},
|
118 |
+
"type": "group"
|
119 |
+
},
|
120 |
+
"file_path": {
|
121 |
+
"default": null,
|
122 |
+
"name": "file_path",
|
123 |
+
"type": {
|
124 |
+
"type": "<class 'str'>"
|
125 |
+
}
|
126 |
+
},
|
127 |
+
"table_name": {
|
128 |
"default": null,
|
129 |
+
"name": "table_name",
|
130 |
"type": {
|
131 |
"type": "<class 'str'>"
|
132 |
}
|
|
|
135 |
"type": "basic"
|
136 |
},
|
137 |
"params": {
|
138 |
+
"columns": "<from file>",
|
139 |
+
"file_format": "csv",
|
140 |
+
"file_path": "uploads/protein.csv",
|
141 |
+
"separator": "<auto>",
|
142 |
+
"table_name": ""
|
143 |
},
|
144 |
"status": "done",
|
145 |
+
"title": "Import file"
|
146 |
},
|
147 |
"dragHandle": ".bg-primary",
|
148 |
+
"height": 487.0,
|
149 |
+
"id": "Import file 1",
|
|
|
150 |
"position": {
|
151 |
+
"x": -755.0582906538923,
|
152 |
+
"y": 543.770372030674
|
153 |
},
|
154 |
"type": "basic",
|
155 |
+
"width": 439.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
},
|
157 |
{
|
158 |
"data": {
|
159 |
+
"__execution_delay": 0.0,
|
160 |
+
"collapsed": null,
|
161 |
"display": null,
|
162 |
"error": null,
|
163 |
"meta": {
|
164 |
"inputs": {},
|
165 |
+
"name": "Import file",
|
166 |
"outputs": {
|
167 |
"output": {
|
168 |
"name": "output",
|
169 |
+
"position": "right",
|
170 |
"type": {
|
171 |
"type": "None"
|
172 |
}
|
173 |
}
|
174 |
},
|
175 |
"params": {
|
176 |
+
"file_format": {
|
177 |
+
"default": "csv",
|
178 |
+
"groups": {
|
179 |
+
"csv": [
|
180 |
+
{
|
181 |
+
"default": "<from file>",
|
182 |
+
"name": "columns",
|
183 |
+
"type": {
|
184 |
+
"type": "<class 'str'>"
|
185 |
+
}
|
186 |
+
},
|
187 |
+
{
|
188 |
+
"default": "<auto>",
|
189 |
+
"name": "separator",
|
190 |
+
"type": {
|
191 |
+
"type": "<class 'str'>"
|
192 |
+
}
|
193 |
+
}
|
194 |
+
],
|
195 |
+
"excel": [
|
196 |
+
{
|
197 |
+
"default": "Sheet1",
|
198 |
+
"name": "sheet_name",
|
199 |
+
"type": {
|
200 |
+
"type": "<class 'str'>"
|
201 |
+
}
|
202 |
+
}
|
203 |
+
],
|
204 |
+
"json": [],
|
205 |
+
"parquet": []
|
206 |
+
},
|
207 |
+
"name": "file_format",
|
208 |
+
"selector": {
|
209 |
+
"default": "csv",
|
210 |
+
"name": "file_format",
|
211 |
+
"type": {
|
212 |
+
"enum": [
|
213 |
+
"csv",
|
214 |
+
"parquet",
|
215 |
+
"json",
|
216 |
+
"excel"
|
217 |
+
]
|
218 |
+
}
|
219 |
+
},
|
220 |
+
"type": "group"
|
221 |
+
},
|
222 |
+
"file_path": {
|
223 |
+
"default": null,
|
224 |
+
"name": "file_path",
|
225 |
"type": {
|
226 |
+
"type": "<class 'str'>"
|
227 |
+
}
|
228 |
+
},
|
229 |
+
"table_name": {
|
230 |
+
"default": null,
|
231 |
+
"name": "table_name",
|
232 |
+
"type": {
|
233 |
+
"type": "<class 'str'>"
|
234 |
}
|
235 |
}
|
236 |
},
|
237 |
"type": "basic"
|
238 |
},
|
239 |
"params": {
|
240 |
+
"columns": "<from file>",
|
241 |
+
"file_format": "csv",
|
242 |
+
"file_path": "uploads/molecules.csv",
|
243 |
+
"separator": "<auto>",
|
244 |
+
"table_name": null
|
245 |
},
|
246 |
"status": "done",
|
247 |
+
"title": "Import file"
|
248 |
},
|
249 |
"dragHandle": ".bg-primary",
|
250 |
+
"height": 436.0,
|
251 |
+
"id": "Import file 2",
|
|
|
252 |
"position": {
|
253 |
+
"x": 62.887657256500006,
|
254 |
+
"y": 1380.6697994924546
|
255 |
},
|
256 |
"type": "basic",
|
257 |
+
"width": 311.0
|
258 |
},
|
259 |
{
|
260 |
"data": {
|
|
|
|
|
261 |
"display": null,
|
262 |
"error": null,
|
263 |
"meta": {
|
264 |
"inputs": {
|
265 |
+
"bundle": {
|
266 |
+
"name": "bundle",
|
267 |
+
"position": "left",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
"type": {
|
269 |
+
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
270 |
}
|
271 |
}
|
272 |
},
|
273 |
+
"name": "Query GenMol",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
274 |
"outputs": {
|
275 |
"output": {
|
276 |
"name": "output",
|
277 |
+
"position": "right",
|
278 |
"type": {
|
279 |
"type": "None"
|
280 |
}
|
281 |
}
|
282 |
},
|
283 |
"params": {
|
284 |
+
"molecule_column": {
|
285 |
+
"default": null,
|
286 |
+
"name": "molecule_column",
|
287 |
"type": {
|
288 |
"type": "<class 'str'>"
|
289 |
}
|
290 |
},
|
291 |
+
"molecule_table": {
|
292 |
+
"default": null,
|
293 |
+
"name": "molecule_table",
|
294 |
"type": {
|
295 |
"type": "<class 'str'>"
|
296 |
}
|
297 |
},
|
298 |
+
"noise": {
|
299 |
+
"default": 0.2,
|
300 |
+
"name": "noise",
|
301 |
"type": {
|
302 |
+
"type": "<class 'float'>"
|
303 |
}
|
304 |
},
|
305 |
+
"num_molecules": {
|
306 |
+
"default": 5.0,
|
307 |
+
"name": "num_molecules",
|
308 |
+
"type": {
|
309 |
+
"type": "<class 'int'>"
|
310 |
+
}
|
311 |
+
},
|
312 |
+
"scoring": {
|
313 |
+
"default": "QED",
|
314 |
+
"name": "scoring",
|
315 |
"type": {
|
316 |
"type": "<class 'str'>"
|
317 |
}
|
318 |
+
},
|
319 |
+
"step_size": {
|
320 |
+
"default": 4.0,
|
321 |
+
"name": "step_size",
|
322 |
+
"type": {
|
323 |
+
"type": "<class 'int'>"
|
324 |
+
}
|
325 |
+
},
|
326 |
+
"temperature": {
|
327 |
+
"default": 1.0,
|
328 |
+
"name": "temperature",
|
329 |
+
"type": {
|
330 |
+
"type": "<class 'float'>"
|
331 |
+
}
|
332 |
}
|
333 |
},
|
334 |
+
"position": {
|
335 |
+
"x": 594.0,
|
336 |
+
"y": 633.0
|
337 |
+
},
|
338 |
"type": "basic"
|
339 |
},
|
340 |
"params": {
|
341 |
+
"molecule_column": null,
|
342 |
+
"molecule_table": null,
|
343 |
+
"noise": 0.2,
|
344 |
+
"num_molecules": 5.0,
|
345 |
+
"scoring": "QED",
|
346 |
+
"step_size": 4.0,
|
347 |
+
"temperature": 1.0
|
348 |
},
|
349 |
"status": "done",
|
350 |
+
"title": "Query GenMol"
|
351 |
},
|
352 |
"dragHandle": ".bg-primary",
|
353 |
+
"height": 601.0,
|
354 |
+
"id": "Query GenMol 1",
|
|
|
355 |
"position": {
|
356 |
+
"x": 663.3333333333335,
|
357 |
+
"y": 1283.3333333333335
|
358 |
},
|
359 |
"type": "basic",
|
360 |
+
"width": 358.0
|
361 |
},
|
362 |
{
|
363 |
"data": {
|
|
|
364 |
"collapsed": null,
|
365 |
"display": null,
|
366 |
"error": null,
|
367 |
"meta": {
|
368 |
+
"inputs": {
|
369 |
+
"bundle": {
|
370 |
+
"name": "bundle",
|
371 |
+
"position": "left",
|
372 |
+
"type": {
|
373 |
+
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
374 |
+
}
|
375 |
+
}
|
376 |
+
},
|
377 |
+
"name": "MSA-search",
|
378 |
"outputs": {
|
379 |
"output": {
|
380 |
"name": "output",
|
381 |
+
"position": "right",
|
382 |
"type": {
|
383 |
"type": "None"
|
384 |
}
|
385 |
}
|
386 |
},
|
387 |
"params": {
|
388 |
+
"databases": {
|
389 |
+
"default": "[\"Uniref30_2302\", \"colabfold_envdb_202108\", \"PDB70_220313\"]",
|
390 |
+
"name": "databases",
|
391 |
"type": {
|
392 |
"type": "<class 'str'>"
|
393 |
}
|
394 |
},
|
395 |
+
"e_value": {
|
396 |
+
"default": 0.0001,
|
397 |
+
"name": "e_value",
|
398 |
"type": {
|
399 |
+
"type": "<class 'float'>"
|
400 |
}
|
401 |
},
|
402 |
+
"iterations": {
|
403 |
+
"default": 1.0,
|
404 |
+
"name": "iterations",
|
405 |
"type": {
|
406 |
+
"type": "<class 'int'>"
|
407 |
}
|
408 |
},
|
409 |
+
"output_alignment_formats": {
|
410 |
+
"default": [
|
411 |
+
"fasta",
|
412 |
+
"a3m"
|
413 |
+
],
|
414 |
+
"name": "output_alignment_formats",
|
415 |
"type": {
|
416 |
+
"type": "list[lynxkite_bio.nims.AlignmentFormats]"
|
417 |
}
|
418 |
+
},
|
419 |
+
"protein_column": {
|
420 |
+
"default": null,
|
421 |
+
"name": "protein_column",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
422 |
"type": {
|
423 |
+
"type": "<class 'str'>"
|
424 |
}
|
425 |
},
|
426 |
+
"protein_table": {
|
427 |
+
"default": null,
|
428 |
+
"name": "protein_table",
|
429 |
"type": {
|
430 |
+
"type": "<class 'str'>"
|
431 |
}
|
432 |
+
},
|
433 |
+
"search_type": {
|
434 |
+
"default": "ALPHAFOLD2",
|
435 |
+
"name": "search_type",
|
|
|
|
|
|
|
436 |
"type": {
|
437 |
+
"enum": [
|
438 |
+
"ALPHAFOLD2",
|
439 |
+
"ESM2"
|
440 |
+
]
|
441 |
}
|
442 |
}
|
443 |
},
|
444 |
+
"position": {
|
445 |
+
"x": 576.0,
|
446 |
+
"y": 228.0
|
|
|
|
|
|
|
|
|
|
|
447 |
},
|
448 |
"type": "basic"
|
449 |
},
|
450 |
+
"params": {
|
451 |
+
"databases": "[\"Uniref30_2302\", \"colabfold_envdb_202108\", \"PDB70_220313\"]",
|
452 |
+
"e_value": 0.0001,
|
453 |
+
"iterations": 1.0,
|
454 |
+
"output_alignment_formats": [
|
455 |
+
"fasta",
|
456 |
+
"a3m"
|
457 |
+
],
|
458 |
+
"protein_column": null,
|
459 |
+
"protein_table": null,
|
460 |
+
"search_type": "ALPHAFOLD2"
|
461 |
+
},
|
462 |
"status": "done",
|
463 |
+
"title": "MSA-search"
|
464 |
},
|
465 |
"dragHandle": ".bg-primary",
|
466 |
+
"height": 550.0,
|
467 |
+
"id": "MSA-search 1",
|
|
|
468 |
"position": {
|
469 |
+
"x": -45.0,
|
470 |
+
"y": 570.0
|
471 |
},
|
472 |
"type": "basic",
|
473 |
+
"width": 531.0
|
474 |
},
|
475 |
{
|
476 |
"data": {
|
|
|
|
|
477 |
"display": null,
|
478 |
"error": null,
|
479 |
"meta": {
|
480 |
"inputs": {
|
481 |
+
"ligands": {
|
482 |
+
"name": "ligands",
|
483 |
+
"position": "left",
|
484 |
"type": {
|
485 |
+
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
486 |
}
|
487 |
},
|
488 |
+
"proteins": {
|
489 |
+
"name": "proteins",
|
490 |
+
"position": "left",
|
491 |
"type": {
|
492 |
+
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
493 |
}
|
494 |
}
|
495 |
},
|
496 |
+
"name": "Query DiffDock",
|
497 |
"outputs": {
|
498 |
"output": {
|
499 |
"name": "output",
|
500 |
+
"position": "right",
|
501 |
"type": {
|
502 |
"type": "None"
|
503 |
}
|
504 |
}
|
505 |
},
|
506 |
"params": {
|
507 |
+
"ligand_column": {
|
508 |
+
"default": null,
|
509 |
+
"name": "ligand_column",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
510 |
"type": {
|
511 |
"type": "<class 'str'>"
|
512 |
}
|
513 |
},
|
514 |
+
"ligand_table": {
|
515 |
+
"default": null,
|
516 |
+
"name": "ligand_table",
|
517 |
"type": {
|
518 |
"type": "<class 'str'>"
|
519 |
}
|
520 |
},
|
521 |
+
"num_poses": {
|
522 |
+
"default": 10.0,
|
523 |
+
"name": "num_poses",
|
524 |
"type": {
|
525 |
+
"type": "<class 'int'>"
|
526 |
}
|
527 |
},
|
528 |
+
"num_steps": {
|
529 |
+
"default": 18.0,
|
530 |
+
"name": "num_steps",
|
531 |
"type": {
|
532 |
"type": "<class 'int'>"
|
533 |
}
|
534 |
},
|
535 |
+
"protein_column": {
|
536 |
+
"default": null,
|
537 |
+
"name": "protein_column",
|
538 |
"type": {
|
539 |
+
"type": "<class 'str'>"
|
540 |
}
|
541 |
},
|
542 |
+
"protein_table": {
|
543 |
+
"default": null,
|
544 |
+
"name": "protein_table",
|
545 |
"type": {
|
546 |
+
"type": "<class 'str'>"
|
547 |
}
|
548 |
},
|
549 |
+
"time_divisions": {
|
550 |
+
"default": 20.0,
|
551 |
+
"name": "time_divisions",
|
552 |
"type": {
|
553 |
+
"type": "<class 'int'>"
|
554 |
}
|
555 |
}
|
556 |
},
|
557 |
"position": {
|
558 |
+
"x": 852.0,
|
559 |
+
"y": 432.0
|
560 |
},
|
561 |
"type": "basic"
|
562 |
},
|
563 |
"params": {
|
564 |
+
"ligand_column": null,
|
565 |
+
"ligand_table": null,
|
566 |
+
"num_poses": 10.0,
|
567 |
+
"num_steps": 18.0,
|
568 |
+
"protein_column": null,
|
569 |
+
"protein_table": null,
|
570 |
+
"time_divisions": 20.0
|
|
|
571 |
},
|
572 |
"status": "done",
|
573 |
+
"title": "Query DiffDock"
|
574 |
},
|
575 |
"dragHandle": ".bg-primary",
|
576 |
+
"height": 635.0,
|
577 |
+
"id": "Query DiffDock 1",
|
578 |
"position": {
|
579 |
+
"x": 1543.010053920781,
|
580 |
+
"y": 1167.386382170133
|
581 |
},
|
582 |
"type": "basic",
|
583 |
+
"width": 408.0
|
584 |
},
|
585 |
{
|
586 |
"data": {
|
587 |
+
"display": {
|
588 |
+
"series": [
|
589 |
+
{
|
590 |
+
"data": [
|
591 |
+
{
|
592 |
+
"name": "Hydrogen",
|
593 |
+
"value": 2
|
594 |
+
},
|
595 |
+
{
|
596 |
+
"name": "Sulfur",
|
597 |
+
"value": 1
|
598 |
+
},
|
599 |
+
{
|
600 |
+
"name": "Oxygen",
|
601 |
+
"value": 4
|
602 |
+
}
|
603 |
+
],
|
604 |
+
"itemStyle": {
|
605 |
+
"borderColor": "#fff",
|
606 |
+
"borderRadius": 10,
|
607 |
+
"borderWidth": 2
|
608 |
+
},
|
609 |
+
"radius": [
|
610 |
+
"40%",
|
611 |
+
"70%"
|
612 |
+
],
|
613 |
+
"type": "pie"
|
614 |
+
}
|
615 |
+
]
|
616 |
+
},
|
617 |
"error": null,
|
618 |
"meta": {
|
619 |
"inputs": {
|
620 |
+
"bundle": {
|
621 |
+
"name": "bundle",
|
622 |
"position": "left",
|
623 |
"type": {
|
624 |
+
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
625 |
}
|
626 |
}
|
627 |
},
|
628 |
+
"name": "View molecules",
|
629 |
+
"outputs": {},
|
630 |
"params": {
|
631 |
+
"color": {
|
632 |
+
"default": "spectrum",
|
633 |
+
"name": "color",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
634 |
"type": {
|
635 |
"type": "<class 'str'>"
|
636 |
}
|
637 |
},
|
638 |
+
"molecule_column": {
|
639 |
+
"default": null,
|
640 |
+
"name": "molecule_column",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
641 |
"type": {
|
642 |
"type": "<class 'str'>"
|
643 |
}
|
644 |
},
|
645 |
+
"molecule_table": {
|
646 |
+
"default": null,
|
647 |
+
"name": "molecule_table",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
648 |
"type": {
|
649 |
"type": "<class 'str'>"
|
650 |
}
|
651 |
}
|
652 |
},
|
653 |
"position": {
|
654 |
+
"x": 1009.0,
|
655 |
+
"y": 124.0
|
656 |
},
|
657 |
+
"type": "visualization"
|
658 |
},
|
659 |
"params": {
|
660 |
+
"color": "spectrum",
|
661 |
+
"molecule_column": null,
|
662 |
+
"molecule_table": null
|
|
|
|
|
|
|
663 |
},
|
664 |
"status": "done",
|
665 |
+
"title": "View molecules"
|
666 |
},
|
667 |
"dragHandle": ".bg-primary",
|
668 |
+
"height": 200.0,
|
669 |
+
"id": "View molecules 3",
|
670 |
"position": {
|
671 |
+
"x": 1545.0,
|
672 |
+
"y": 585.0
|
673 |
},
|
674 |
+
"type": "visualization",
|
675 |
+
"width": 200.0
|
676 |
},
|
677 |
{
|
678 |
"data": {
|
679 |
+
"display": {
|
680 |
+
"series": [
|
681 |
+
{
|
682 |
+
"data": [
|
683 |
+
{
|
684 |
+
"name": "Hydrogen",
|
685 |
+
"value": 2
|
686 |
+
},
|
687 |
+
{
|
688 |
+
"name": "Sulfur",
|
689 |
+
"value": 1
|
690 |
+
},
|
691 |
+
{
|
692 |
+
"name": "Oxygen",
|
693 |
+
"value": 4
|
694 |
+
}
|
695 |
+
],
|
696 |
+
"itemStyle": {
|
697 |
+
"borderColor": "#fff",
|
698 |
+
"borderRadius": 10,
|
699 |
+
"borderWidth": 2
|
700 |
+
},
|
701 |
+
"radius": [
|
702 |
+
"40%",
|
703 |
+
"70%"
|
704 |
+
],
|
705 |
+
"type": "pie"
|
706 |
+
}
|
707 |
+
]
|
708 |
+
},
|
709 |
"error": null,
|
710 |
"meta": {
|
711 |
+
"inputs": {
|
712 |
+
"bundle": {
|
713 |
+
"name": "bundle",
|
714 |
+
"position": "left",
|
|
|
|
|
715 |
"type": {
|
716 |
+
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
717 |
}
|
718 |
}
|
719 |
},
|
720 |
+
"name": "View molecules",
|
721 |
+
"outputs": {},
|
722 |
"params": {
|
723 |
+
"color": {
|
724 |
+
"default": "spectrum",
|
725 |
+
"name": "color",
|
726 |
"type": {
|
727 |
"type": "<class 'str'>"
|
728 |
}
|
729 |
},
|
730 |
+
"molecule_column": {
|
731 |
+
"default": null,
|
732 |
+
"name": "molecule_column",
|
733 |
"type": {
|
734 |
+
"type": "<class 'str'>"
|
735 |
}
|
736 |
},
|
737 |
+
"molecule_table": {
|
738 |
+
"default": null,
|
739 |
+
"name": "molecule_table",
|
740 |
"type": {
|
741 |
"type": "<class 'str'>"
|
742 |
}
|
743 |
}
|
744 |
},
|
745 |
"position": {
|
746 |
+
"x": 859.0,
|
747 |
+
"y": 225.0
|
748 |
},
|
749 |
+
"type": "visualization"
|
750 |
},
|
751 |
"params": {
|
752 |
+
"color": "spectrum",
|
753 |
+
"molecule_column": null,
|
754 |
+
"molecule_table": null
|
755 |
},
|
756 |
"status": "done",
|
757 |
+
"title": "View molecules"
|
758 |
},
|
759 |
"dragHandle": ".bg-primary",
|
760 |
+
"height": 200.0,
|
761 |
+
"id": "View molecules 1",
|
762 |
"position": {
|
763 |
+
"x": 2230.0,
|
764 |
+
"y": 1598.3333333333333
|
765 |
},
|
766 |
+
"type": "visualization",
|
767 |
+
"width": 200.0
|
768 |
},
|
769 |
{
|
770 |
"data": {
|
|
|
772 |
"error": null,
|
773 |
"meta": {
|
774 |
"inputs": {
|
775 |
+
"bundle": {
|
776 |
+
"name": "bundle",
|
777 |
"position": "left",
|
778 |
"type": {
|
779 |
+
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
780 |
}
|
781 |
}
|
782 |
},
|
783 |
+
"name": "Query OpenFold2",
|
784 |
"outputs": {
|
785 |
"output": {
|
786 |
"name": "output",
|
787 |
+
"position": "right",
|
788 |
"type": {
|
789 |
"type": "None"
|
790 |
}
|
791 |
}
|
792 |
},
|
793 |
"params": {
|
794 |
+
"alignment_column": {
|
795 |
+
"default": null,
|
796 |
+
"name": "alignment_column",
|
797 |
+
"type": {
|
798 |
+
"type": "<class 'str'>"
|
799 |
+
}
|
800 |
+
},
|
801 |
+
"alignment_table": {
|
802 |
+
"default": null,
|
803 |
+
"name": "alignment_table",
|
804 |
+
"type": {
|
805 |
+
"type": "<class 'str'>"
|
806 |
+
}
|
807 |
+
},
|
808 |
+
"databases": {
|
809 |
+
"default": "[\"Uniref30_2302\", \"colabfold_envdb_202108\", \"PDB70_220313\"]",
|
810 |
+
"name": "databases",
|
811 |
"type": {
|
812 |
"type": "<class 'str'>"
|
813 |
}
|
814 |
},
|
815 |
+
"protein_column": {
|
816 |
+
"default": null,
|
817 |
+
"name": "protein_column",
|
818 |
+
"type": {
|
819 |
+
"type": "<class 'str'>"
|
820 |
+
}
|
821 |
+
},
|
822 |
+
"protein_table": {
|
823 |
+
"default": null,
|
824 |
+
"name": "protein_table",
|
825 |
"type": {
|
826 |
"type": "<class 'str'>"
|
827 |
}
|
828 |
+
},
|
829 |
+
"relaxed_prediction": {
|
830 |
+
"default": false,
|
831 |
+
"name": "relaxed_prediction",
|
832 |
+
"type": {
|
833 |
+
"type": "<class 'bool'>"
|
834 |
+
}
|
835 |
+
},
|
836 |
+
"use_templates": {
|
837 |
+
"default": false,
|
838 |
+
"name": "use_templates",
|
839 |
+
"type": {
|
840 |
+
"type": "<class 'bool'>"
|
841 |
+
}
|
842 |
}
|
843 |
},
|
844 |
"position": {
|
845 |
+
"x": 628.0,
|
846 |
+
"y": 184.0
|
847 |
},
|
848 |
"type": "basic"
|
849 |
},
|
850 |
"params": {
|
851 |
+
"alignment_column": null,
|
852 |
+
"alignment_table": null,
|
853 |
+
"databases": "[\"Uniref30_2302\", \"colabfold_envdb_202108\", \"PDB70_220313\"]",
|
854 |
+
"protein_column": null,
|
855 |
+
"protein_table": null,
|
856 |
+
"relaxed_prediction": false,
|
857 |
+
"use_templates": false
|
858 |
},
|
859 |
"status": "done",
|
860 |
+
"title": "Query OpenFold2"
|
861 |
},
|
862 |
"dragHandle": ".bg-primary",
|
863 |
+
"height": 653.0,
|
864 |
+
"id": "Query OpenFold2 1",
|
865 |
"position": {
|
866 |
+
"x": 750.0,
|
867 |
+
"y": 480.0
|
868 |
},
|
869 |
"type": "basic",
|
870 |
+
"width": 523.0
|
871 |
}
|
872 |
]
|
873 |
}
|
examples/{Graph RAG → Graph RAG.lynxkite.json}
RENAMED
@@ -510,8 +510,7 @@
|
|
510 |
"title": "Ask LLM",
|
511 |
"params": {
|
512 |
"max_tokens": 100.0,
|
513 |
-
"accepted_regex": ""
|
514 |
-
"model": "SultanR/SmolTulu-1.7b-Instruct"
|
515 |
},
|
516 |
"display": null,
|
517 |
"error": null,
|
@@ -541,13 +540,6 @@
|
|
541 |
"type": {
|
542 |
"type": "<class 'int'>"
|
543 |
}
|
544 |
-
},
|
545 |
-
"model": {
|
546 |
-
"type": {
|
547 |
-
"type": "<class 'str'>"
|
548 |
-
},
|
549 |
-
"default": null,
|
550 |
-
"name": "model"
|
551 |
}
|
552 |
},
|
553 |
"outputs": {
|
|
|
510 |
"title": "Ask LLM",
|
511 |
"params": {
|
512 |
"max_tokens": 100.0,
|
513 |
+
"accepted_regex": ""
|
|
|
514 |
},
|
515 |
"display": null,
|
516 |
"error": null,
|
|
|
540 |
"type": {
|
541 |
"type": "<class 'int'>"
|
542 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
543 |
}
|
544 |
},
|
545 |
"outputs": {
|
examples/{Image processing → Image processing.lynxkite.json}
RENAMED
File without changes
|
examples/Model definition.lynxkite.json
ADDED
@@ -0,0 +1,614 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"edges": [
|
3 |
+
{
|
4 |
+
"id": "MSE loss 2 Optimizer 2",
|
5 |
+
"source": "MSE loss 2",
|
6 |
+
"sourceHandle": "output",
|
7 |
+
"target": "Optimizer 2",
|
8 |
+
"targetHandle": "loss"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "Activation 1 Repeat 1",
|
12 |
+
"source": "Activation 1",
|
13 |
+
"sourceHandle": "output",
|
14 |
+
"target": "Repeat 1",
|
15 |
+
"targetHandle": "input"
|
16 |
+
},
|
17 |
+
{
|
18 |
+
"id": "Linear 1 Activation 1",
|
19 |
+
"source": "Linear 1",
|
20 |
+
"sourceHandle": "output",
|
21 |
+
"target": "Activation 1",
|
22 |
+
"targetHandle": "x"
|
23 |
+
},
|
24 |
+
{
|
25 |
+
"id": "Repeat 1 Linear 1",
|
26 |
+
"source": "Repeat 1",
|
27 |
+
"sourceHandle": "output",
|
28 |
+
"target": "Linear 1",
|
29 |
+
"targetHandle": "x"
|
30 |
+
},
|
31 |
+
{
|
32 |
+
"id": "Input: tensor 1 Linear 1",
|
33 |
+
"source": "Input: tensor 1",
|
34 |
+
"sourceHandle": "output",
|
35 |
+
"target": "Linear 1",
|
36 |
+
"targetHandle": "x"
|
37 |
+
},
|
38 |
+
{
|
39 |
+
"id": "Constant vector 1 Add 1",
|
40 |
+
"source": "Constant vector 1",
|
41 |
+
"sourceHandle": "output",
|
42 |
+
"target": "Add 1",
|
43 |
+
"targetHandle": "b"
|
44 |
+
},
|
45 |
+
{
|
46 |
+
"id": "Input: tensor 3 Add 1",
|
47 |
+
"source": "Input: tensor 3",
|
48 |
+
"sourceHandle": "output",
|
49 |
+
"target": "Add 1",
|
50 |
+
"targetHandle": "a"
|
51 |
+
},
|
52 |
+
{
|
53 |
+
"id": "Add 1 MSE loss 2",
|
54 |
+
"source": "Add 1",
|
55 |
+
"sourceHandle": "output",
|
56 |
+
"target": "MSE loss 2",
|
57 |
+
"targetHandle": "y"
|
58 |
+
},
|
59 |
+
{
|
60 |
+
"id": "Activation 1 Output 1",
|
61 |
+
"source": "Activation 1",
|
62 |
+
"sourceHandle": "output",
|
63 |
+
"target": "Output 1",
|
64 |
+
"targetHandle": "x"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"id": "Output 1 MSE loss 2",
|
68 |
+
"source": "Output 1",
|
69 |
+
"sourceHandle": "x",
|
70 |
+
"target": "MSE loss 2",
|
71 |
+
"targetHandle": "x"
|
72 |
+
}
|
73 |
+
],
|
74 |
+
"env": "PyTorch model",
|
75 |
+
"nodes": [
|
76 |
+
{
|
77 |
+
"data": {
|
78 |
+
"__execution_delay": 0.0,
|
79 |
+
"collapsed": null,
|
80 |
+
"display": null,
|
81 |
+
"error": null,
|
82 |
+
"input_metadata": null,
|
83 |
+
"meta": {
|
84 |
+
"inputs": {
|
85 |
+
"loss": {
|
86 |
+
"name": "loss",
|
87 |
+
"position": "bottom",
|
88 |
+
"type": {
|
89 |
+
"type": "tensor"
|
90 |
+
}
|
91 |
+
}
|
92 |
+
},
|
93 |
+
"name": "Optimizer",
|
94 |
+
"outputs": {},
|
95 |
+
"params": {
|
96 |
+
"lr": {
|
97 |
+
"default": 0.001,
|
98 |
+
"name": "lr",
|
99 |
+
"type": {
|
100 |
+
"type": "<class 'float'>"
|
101 |
+
}
|
102 |
+
},
|
103 |
+
"type": {
|
104 |
+
"default": "AdamW",
|
105 |
+
"name": "type",
|
106 |
+
"type": {
|
107 |
+
"enum": [
|
108 |
+
"AdamW",
|
109 |
+
"Adafactor",
|
110 |
+
"Adagrad",
|
111 |
+
"SGD",
|
112 |
+
"Lion",
|
113 |
+
"Paged AdamW",
|
114 |
+
"Galore AdamW"
|
115 |
+
]
|
116 |
+
}
|
117 |
+
}
|
118 |
+
},
|
119 |
+
"type": "basic"
|
120 |
+
},
|
121 |
+
"params": {
|
122 |
+
"lr": "0.1",
|
123 |
+
"type": "SGD"
|
124 |
+
},
|
125 |
+
"status": "planned",
|
126 |
+
"title": "Optimizer"
|
127 |
+
},
|
128 |
+
"dragHandle": ".bg-primary",
|
129 |
+
"height": 250.0,
|
130 |
+
"id": "Optimizer 2",
|
131 |
+
"position": {
|
132 |
+
"x": 359.75221367487865,
|
133 |
+
"y": -1560.7604266065723
|
134 |
+
},
|
135 |
+
"type": "basic",
|
136 |
+
"width": 232.0
|
137 |
+
},
|
138 |
+
{
|
139 |
+
"data": {
|
140 |
+
"__execution_delay": 0.0,
|
141 |
+
"collapsed": null,
|
142 |
+
"display": null,
|
143 |
+
"error": null,
|
144 |
+
"input_metadata": null,
|
145 |
+
"meta": {
|
146 |
+
"inputs": {
|
147 |
+
"x": {
|
148 |
+
"name": "x",
|
149 |
+
"position": "bottom",
|
150 |
+
"type": {
|
151 |
+
"type": "<class 'inspect._empty'>"
|
152 |
+
}
|
153 |
+
}
|
154 |
+
},
|
155 |
+
"name": "Activation",
|
156 |
+
"outputs": {
|
157 |
+
"output": {
|
158 |
+
"name": "output",
|
159 |
+
"position": "top",
|
160 |
+
"type": {
|
161 |
+
"type": "None"
|
162 |
+
}
|
163 |
+
}
|
164 |
+
},
|
165 |
+
"params": {
|
166 |
+
"type": {
|
167 |
+
"default": "ReLU",
|
168 |
+
"name": "type",
|
169 |
+
"type": {
|
170 |
+
"enum": [
|
171 |
+
"ReLU",
|
172 |
+
"Leaky_ReLU",
|
173 |
+
"Tanh",
|
174 |
+
"Mish"
|
175 |
+
]
|
176 |
+
}
|
177 |
+
}
|
178 |
+
},
|
179 |
+
"type": "basic"
|
180 |
+
},
|
181 |
+
"params": {
|
182 |
+
"type": "Leaky_ReLU"
|
183 |
+
},
|
184 |
+
"status": "planned",
|
185 |
+
"title": "Activation"
|
186 |
+
},
|
187 |
+
"dragHandle": ".bg-primary",
|
188 |
+
"height": 200.0,
|
189 |
+
"id": "Activation 1",
|
190 |
+
"position": {
|
191 |
+
"x": 99.77615018185415,
|
192 |
+
"y": -249.43925929074078
|
193 |
+
},
|
194 |
+
"type": "basic",
|
195 |
+
"width": 200.0
|
196 |
+
},
|
197 |
+
{
|
198 |
+
"data": {
|
199 |
+
"__execution_delay": 0.0,
|
200 |
+
"collapsed": null,
|
201 |
+
"display": null,
|
202 |
+
"error": null,
|
203 |
+
"input_metadata": null,
|
204 |
+
"meta": {
|
205 |
+
"inputs": {},
|
206 |
+
"name": "Input: tensor",
|
207 |
+
"outputs": {
|
208 |
+
"output": {
|
209 |
+
"name": "output",
|
210 |
+
"position": "top",
|
211 |
+
"type": {
|
212 |
+
"type": "tensor"
|
213 |
+
}
|
214 |
+
}
|
215 |
+
},
|
216 |
+
"params": {
|
217 |
+
"name": {
|
218 |
+
"default": null,
|
219 |
+
"name": "name",
|
220 |
+
"type": {
|
221 |
+
"type": "None"
|
222 |
+
}
|
223 |
+
}
|
224 |
+
},
|
225 |
+
"type": "basic"
|
226 |
+
},
|
227 |
+
"params": {
|
228 |
+
"name": "Y"
|
229 |
+
},
|
230 |
+
"status": "planned",
|
231 |
+
"title": "Input: tensor"
|
232 |
+
},
|
233 |
+
"dragHandle": ".bg-primary",
|
234 |
+
"height": 200.0,
|
235 |
+
"id": "Input: tensor 3",
|
236 |
+
"position": {
|
237 |
+
"x": 485.8840220312055,
|
238 |
+
"y": -268.0485936515193
|
239 |
+
},
|
240 |
+
"type": "basic",
|
241 |
+
"width": 200.0
|
242 |
+
},
|
243 |
+
{
|
244 |
+
"data": {
|
245 |
+
"__execution_delay": null,
|
246 |
+
"collapsed": true,
|
247 |
+
"display": null,
|
248 |
+
"error": null,
|
249 |
+
"input_metadata": null,
|
250 |
+
"meta": {
|
251 |
+
"inputs": {
|
252 |
+
"x": {
|
253 |
+
"name": "x",
|
254 |
+
"position": "bottom",
|
255 |
+
"type": {
|
256 |
+
"type": "<class 'inspect._empty'>"
|
257 |
+
}
|
258 |
+
},
|
259 |
+
"y": {
|
260 |
+
"name": "y",
|
261 |
+
"position": "bottom",
|
262 |
+
"type": {
|
263 |
+
"type": "<class 'inspect._empty'>"
|
264 |
+
}
|
265 |
+
}
|
266 |
+
},
|
267 |
+
"name": "MSE loss",
|
268 |
+
"outputs": {
|
269 |
+
"output": {
|
270 |
+
"name": "output",
|
271 |
+
"position": "top",
|
272 |
+
"type": {
|
273 |
+
"type": "None"
|
274 |
+
}
|
275 |
+
}
|
276 |
+
},
|
277 |
+
"params": {},
|
278 |
+
"type": "basic"
|
279 |
+
},
|
280 |
+
"params": {},
|
281 |
+
"status": "planned",
|
282 |
+
"title": "MSE loss"
|
283 |
+
},
|
284 |
+
"dragHandle": ".bg-primary",
|
285 |
+
"height": 200.0,
|
286 |
+
"id": "MSE loss 2",
|
287 |
+
"position": {
|
288 |
+
"x": 384.54674698852955,
|
289 |
+
"y": -1184.4701545316577
|
290 |
+
},
|
291 |
+
"type": "basic",
|
292 |
+
"width": 200.0
|
293 |
+
},
|
294 |
+
{
|
295 |
+
"data": {
|
296 |
+
"__execution_delay": 0.0,
|
297 |
+
"collapsed": null,
|
298 |
+
"display": null,
|
299 |
+
"error": null,
|
300 |
+
"input_metadata": null,
|
301 |
+
"meta": {
|
302 |
+
"inputs": {
|
303 |
+
"input": {
|
304 |
+
"name": "input",
|
305 |
+
"position": "top",
|
306 |
+
"type": {
|
307 |
+
"type": "tensor"
|
308 |
+
}
|
309 |
+
}
|
310 |
+
},
|
311 |
+
"name": "Repeat",
|
312 |
+
"outputs": {
|
313 |
+
"output": {
|
314 |
+
"name": "output",
|
315 |
+
"position": "bottom",
|
316 |
+
"type": {
|
317 |
+
"type": "tensor"
|
318 |
+
}
|
319 |
+
}
|
320 |
+
},
|
321 |
+
"params": {
|
322 |
+
"same_weights": {
|
323 |
+
"default": false,
|
324 |
+
"name": "same_weights",
|
325 |
+
"type": {
|
326 |
+
"type": "<class 'bool'>"
|
327 |
+
}
|
328 |
+
},
|
329 |
+
"times": {
|
330 |
+
"default": 1.0,
|
331 |
+
"name": "times",
|
332 |
+
"type": {
|
333 |
+
"type": "<class 'int'>"
|
334 |
+
}
|
335 |
+
}
|
336 |
+
},
|
337 |
+
"type": "basic"
|
338 |
+
},
|
339 |
+
"params": {
|
340 |
+
"same_weights": false,
|
341 |
+
"times": "2"
|
342 |
+
},
|
343 |
+
"status": "planned",
|
344 |
+
"title": "Repeat"
|
345 |
+
},
|
346 |
+
"dragHandle": ".bg-primary",
|
347 |
+
"height": 200.0,
|
348 |
+
"id": "Repeat 1",
|
349 |
+
"position": {
|
350 |
+
"x": -210.0,
|
351 |
+
"y": -135.0
|
352 |
+
},
|
353 |
+
"type": "basic",
|
354 |
+
"width": 200.0
|
355 |
+
},
|
356 |
+
{
|
357 |
+
"data": {
|
358 |
+
"__execution_delay": 0.0,
|
359 |
+
"collapsed": null,
|
360 |
+
"display": null,
|
361 |
+
"error": null,
|
362 |
+
"input_metadata": null,
|
363 |
+
"meta": {
|
364 |
+
"inputs": {
|
365 |
+
"x": {
|
366 |
+
"name": "x",
|
367 |
+
"position": "bottom",
|
368 |
+
"type": {
|
369 |
+
"type": "<class 'inspect._empty'>"
|
370 |
+
}
|
371 |
+
}
|
372 |
+
},
|
373 |
+
"name": "Linear",
|
374 |
+
"outputs": {
|
375 |
+
"output": {
|
376 |
+
"name": "output",
|
377 |
+
"position": "top",
|
378 |
+
"type": {
|
379 |
+
"type": "None"
|
380 |
+
}
|
381 |
+
}
|
382 |
+
},
|
383 |
+
"params": {
|
384 |
+
"output_dim": {
|
385 |
+
"default": 1024.0,
|
386 |
+
"name": "output_dim",
|
387 |
+
"type": {
|
388 |
+
"type": "<class 'int'>"
|
389 |
+
}
|
390 |
+
}
|
391 |
+
},
|
392 |
+
"type": "basic"
|
393 |
+
},
|
394 |
+
"params": {
|
395 |
+
"output_dim": "4"
|
396 |
+
},
|
397 |
+
"status": "planned",
|
398 |
+
"title": "Linear"
|
399 |
+
},
|
400 |
+
"dragHandle": ".bg-primary",
|
401 |
+
"height": 200.0,
|
402 |
+
"id": "Linear 1",
|
403 |
+
"position": {
|
404 |
+
"x": 98.54861342271252,
|
405 |
+
"y": 14.121603973834155
|
406 |
+
},
|
407 |
+
"type": "basic",
|
408 |
+
"width": 200.0
|
409 |
+
},
|
410 |
+
{
|
411 |
+
"data": {
|
412 |
+
"__execution_delay": 0.0,
|
413 |
+
"collapsed": null,
|
414 |
+
"display": null,
|
415 |
+
"error": null,
|
416 |
+
"input_metadata": null,
|
417 |
+
"meta": {
|
418 |
+
"inputs": {},
|
419 |
+
"name": "Input: tensor",
|
420 |
+
"outputs": {
|
421 |
+
"output": {
|
422 |
+
"name": "output",
|
423 |
+
"position": "top",
|
424 |
+
"type": {
|
425 |
+
"type": "tensor"
|
426 |
+
}
|
427 |
+
}
|
428 |
+
},
|
429 |
+
"params": {
|
430 |
+
"name": {
|
431 |
+
"default": null,
|
432 |
+
"name": "name",
|
433 |
+
"type": {
|
434 |
+
"type": "None"
|
435 |
+
}
|
436 |
+
}
|
437 |
+
},
|
438 |
+
"type": "basic"
|
439 |
+
},
|
440 |
+
"params": {
|
441 |
+
"name": "X"
|
442 |
+
},
|
443 |
+
"status": "planned",
|
444 |
+
"title": "Input: tensor"
|
445 |
+
},
|
446 |
+
"dragHandle": ".bg-primary",
|
447 |
+
"height": 200.0,
|
448 |
+
"id": "Input: tensor 1",
|
449 |
+
"position": {
|
450 |
+
"x": 108.75735538875443,
|
451 |
+
"y": 331.53404347930933
|
452 |
+
},
|
453 |
+
"type": "basic",
|
454 |
+
"width": 200.0
|
455 |
+
},
|
456 |
+
{
|
457 |
+
"data": {
|
458 |
+
"__execution_delay": 0.0,
|
459 |
+
"collapsed": null,
|
460 |
+
"display": null,
|
461 |
+
"error": null,
|
462 |
+
"input_metadata": null,
|
463 |
+
"meta": {
|
464 |
+
"inputs": {},
|
465 |
+
"name": "Constant vector",
|
466 |
+
"outputs": {
|
467 |
+
"output": {
|
468 |
+
"name": "output",
|
469 |
+
"position": "top",
|
470 |
+
"type": {
|
471 |
+
"type": "None"
|
472 |
+
}
|
473 |
+
}
|
474 |
+
},
|
475 |
+
"params": {
|
476 |
+
"size": {
|
477 |
+
"default": 1.0,
|
478 |
+
"name": "size",
|
479 |
+
"type": {
|
480 |
+
"type": "<class 'int'>"
|
481 |
+
}
|
482 |
+
},
|
483 |
+
"value": {
|
484 |
+
"default": 0.0,
|
485 |
+
"name": "value",
|
486 |
+
"type": {
|
487 |
+
"type": "<class 'int'>"
|
488 |
+
}
|
489 |
+
}
|
490 |
+
},
|
491 |
+
"type": "basic"
|
492 |
+
},
|
493 |
+
"params": {
|
494 |
+
"size": "1",
|
495 |
+
"value": "1"
|
496 |
+
},
|
497 |
+
"status": "planned",
|
498 |
+
"title": "Constant vector"
|
499 |
+
},
|
500 |
+
"dragHandle": ".bg-primary",
|
501 |
+
"height": 258.0,
|
502 |
+
"id": "Constant vector 1",
|
503 |
+
"position": {
|
504 |
+
"x": 886.708922897265,
|
505 |
+
"y": -298.4394167425953
|
506 |
+
},
|
507 |
+
"type": "basic",
|
508 |
+
"width": 238.0
|
509 |
+
},
|
510 |
+
{
|
511 |
+
"data": {
|
512 |
+
"__execution_delay": null,
|
513 |
+
"collapsed": true,
|
514 |
+
"display": null,
|
515 |
+
"error": null,
|
516 |
+
"input_metadata": null,
|
517 |
+
"meta": {
|
518 |
+
"inputs": {
|
519 |
+
"a": {
|
520 |
+
"name": "a",
|
521 |
+
"position": "bottom",
|
522 |
+
"type": {
|
523 |
+
"type": "<class 'inspect._empty'>"
|
524 |
+
}
|
525 |
+
},
|
526 |
+
"b": {
|
527 |
+
"name": "b",
|
528 |
+
"position": "bottom",
|
529 |
+
"type": {
|
530 |
+
"type": "<class 'inspect._empty'>"
|
531 |
+
}
|
532 |
+
}
|
533 |
+
},
|
534 |
+
"name": "Add",
|
535 |
+
"outputs": {
|
536 |
+
"output": {
|
537 |
+
"name": "output",
|
538 |
+
"position": "top",
|
539 |
+
"type": {
|
540 |
+
"type": "None"
|
541 |
+
}
|
542 |
+
}
|
543 |
+
},
|
544 |
+
"params": {},
|
545 |
+
"type": "basic"
|
546 |
+
},
|
547 |
+
"params": {},
|
548 |
+
"status": "planned",
|
549 |
+
"title": "Add"
|
550 |
+
},
|
551 |
+
"dragHandle": ".bg-primary",
|
552 |
+
"height": 200.0,
|
553 |
+
"id": "Add 1",
|
554 |
+
"position": {
|
555 |
+
"x": 722.1292469875319,
|
556 |
+
"y": -762.6853551968964
|
557 |
+
},
|
558 |
+
"type": "basic",
|
559 |
+
"width": 200.0
|
560 |
+
},
|
561 |
+
{
|
562 |
+
"data": {
|
563 |
+
"__execution_delay": null,
|
564 |
+
"collapsed": true,
|
565 |
+
"display": null,
|
566 |
+
"error": null,
|
567 |
+
"input_metadata": null,
|
568 |
+
"meta": {
|
569 |
+
"inputs": {
|
570 |
+
"x": {
|
571 |
+
"name": "x",
|
572 |
+
"position": "bottom",
|
573 |
+
"type": {
|
574 |
+
"type": "tensor"
|
575 |
+
}
|
576 |
+
}
|
577 |
+
},
|
578 |
+
"name": "Output",
|
579 |
+
"outputs": {
|
580 |
+
"x": {
|
581 |
+
"name": "x",
|
582 |
+
"position": "top",
|
583 |
+
"type": {
|
584 |
+
"type": "tensor"
|
585 |
+
}
|
586 |
+
}
|
587 |
+
},
|
588 |
+
"params": {
|
589 |
+
"name": {
|
590 |
+
"default": null,
|
591 |
+
"name": "name",
|
592 |
+
"type": {
|
593 |
+
"type": "None"
|
594 |
+
}
|
595 |
+
}
|
596 |
+
},
|
597 |
+
"type": "basic"
|
598 |
+
},
|
599 |
+
"params": {},
|
600 |
+
"status": "planned",
|
601 |
+
"title": "Output"
|
602 |
+
},
|
603 |
+
"dragHandle": ".bg-primary",
|
604 |
+
"height": 200.0,
|
605 |
+
"id": "Output 1",
|
606 |
+
"position": {
|
607 |
+
"x": 185.15239170944702,
|
608 |
+
"y": -733.1526319565451
|
609 |
+
},
|
610 |
+
"type": "basic",
|
611 |
+
"width": 200.0
|
612 |
+
}
|
613 |
+
]
|
614 |
+
}
|
examples/Model use.lynxkite.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
examples/{NetworkX demo → NetworkX demo.lynxkite.json}
RENAMED
File without changes
|
examples/ODE-GNN experiment.lynxkite.json
ADDED
@@ -0,0 +1,466 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"edges": [
|
3 |
+
{
|
4 |
+
"id": "Import CSV 1 Train/test split 1",
|
5 |
+
"source": "Import CSV 1",
|
6 |
+
"sourceHandle": "output",
|
7 |
+
"target": "Train/test split 1",
|
8 |
+
"targetHandle": "bundle"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "Train/test split 1 Create graph 1",
|
12 |
+
"source": "Train/test split 1",
|
13 |
+
"sourceHandle": "output",
|
14 |
+
"target": "Create graph 1",
|
15 |
+
"targetHandle": "bundle"
|
16 |
+
},
|
17 |
+
{
|
18 |
+
"id": "Biomedical foundation graph (PLACEHOLDER) 1 Create graph 1",
|
19 |
+
"source": "Biomedical foundation graph (PLACEHOLDER) 1",
|
20 |
+
"sourceHandle": "output",
|
21 |
+
"target": "Create graph 1",
|
22 |
+
"targetHandle": "bundle"
|
23 |
+
},
|
24 |
+
{
|
25 |
+
"id": "Define model 1 Create graph 1",
|
26 |
+
"source": "Define model 1",
|
27 |
+
"sourceHandle": "output",
|
28 |
+
"target": "Create graph 1",
|
29 |
+
"targetHandle": "bundle"
|
30 |
+
},
|
31 |
+
{
|
32 |
+
"id": "Create graph 1 Train model 1",
|
33 |
+
"source": "Create graph 1",
|
34 |
+
"sourceHandle": "output",
|
35 |
+
"target": "Train model 1",
|
36 |
+
"targetHandle": "bundle"
|
37 |
+
},
|
38 |
+
{
|
39 |
+
"id": "Train model 1 Model inference 1",
|
40 |
+
"source": "Train model 1",
|
41 |
+
"sourceHandle": "output",
|
42 |
+
"target": "Model inference 1",
|
43 |
+
"targetHandle": "bundle"
|
44 |
+
}
|
45 |
+
],
|
46 |
+
"env": "LynxKite Graph Analytics",
|
47 |
+
"nodes": [
|
48 |
+
{
|
49 |
+
"data": {
|
50 |
+
"__execution_delay": 0.0,
|
51 |
+
"collapsed": null,
|
52 |
+
"display": null,
|
53 |
+
"error": null,
|
54 |
+
"meta": {
|
55 |
+
"inputs": {},
|
56 |
+
"name": "Biomedical foundation graph (PLACEHOLDER)",
|
57 |
+
"outputs": {
|
58 |
+
"output": {
|
59 |
+
"name": "output",
|
60 |
+
"position": "right",
|
61 |
+
"type": {
|
62 |
+
"type": "None"
|
63 |
+
}
|
64 |
+
}
|
65 |
+
},
|
66 |
+
"params": {
|
67 |
+
"filter_nodes": {
|
68 |
+
"default": null,
|
69 |
+
"name": "filter_nodes",
|
70 |
+
"type": {
|
71 |
+
"type": "<class 'str'>"
|
72 |
+
}
|
73 |
+
}
|
74 |
+
},
|
75 |
+
"type": "basic"
|
76 |
+
},
|
77 |
+
"params": {
|
78 |
+
"filter_nodes": "drug,gene,disease"
|
79 |
+
},
|
80 |
+
"status": "done",
|
81 |
+
"title": "Biomedical foundation graph (PLACEHOLDER)"
|
82 |
+
},
|
83 |
+
"dragHandle": ".bg-primary",
|
84 |
+
"height": 200.0,
|
85 |
+
"id": "Biomedical foundation graph (PLACEHOLDER) 1",
|
86 |
+
"position": {
|
87 |
+
"x": 230.1082040835347,
|
88 |
+
"y": 643.2454063689602
|
89 |
+
},
|
90 |
+
"type": "basic",
|
91 |
+
"width": 200.0
|
92 |
+
},
|
93 |
+
{
|
94 |
+
"data": {
|
95 |
+
"__execution_delay": null,
|
96 |
+
"collapsed": true,
|
97 |
+
"display": null,
|
98 |
+
"error": "Missing input: bundle",
|
99 |
+
"meta": {
|
100 |
+
"inputs": {
|
101 |
+
"bundle": {
|
102 |
+
"name": "bundle",
|
103 |
+
"position": "left",
|
104 |
+
"type": {
|
105 |
+
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
106 |
+
}
|
107 |
+
}
|
108 |
+
},
|
109 |
+
"name": "Train/test split",
|
110 |
+
"outputs": {
|
111 |
+
"output": {
|
112 |
+
"name": "output",
|
113 |
+
"position": "right",
|
114 |
+
"type": {
|
115 |
+
"type": "None"
|
116 |
+
}
|
117 |
+
}
|
118 |
+
},
|
119 |
+
"params": {
|
120 |
+
"table_name": {
|
121 |
+
"default": null,
|
122 |
+
"name": "table_name",
|
123 |
+
"type": {
|
124 |
+
"type": "<class 'str'>"
|
125 |
+
}
|
126 |
+
},
|
127 |
+
"test_ratio": {
|
128 |
+
"default": 0.1,
|
129 |
+
"name": "test_ratio",
|
130 |
+
"type": {
|
131 |
+
"type": "<class 'float'>"
|
132 |
+
}
|
133 |
+
}
|
134 |
+
},
|
135 |
+
"type": "basic"
|
136 |
+
},
|
137 |
+
"params": {
|
138 |
+
"table_name": null,
|
139 |
+
"test_ratio": 0.1
|
140 |
+
},
|
141 |
+
"status": "planned",
|
142 |
+
"title": "Train/test split"
|
143 |
+
},
|
144 |
+
"dragHandle": ".bg-primary",
|
145 |
+
"height": 200.0,
|
146 |
+
"id": "Train/test split 1",
|
147 |
+
"position": {
|
148 |
+
"x": 313.3745540124723,
|
149 |
+
"y": 412.5466021460861
|
150 |
+
},
|
151 |
+
"type": "basic",
|
152 |
+
"width": 200.0
|
153 |
+
},
|
154 |
+
{
|
155 |
+
"data": {
|
156 |
+
"display": null,
|
157 |
+
"error": "[Errno 2] No such file or directory: ''",
|
158 |
+
"meta": {
|
159 |
+
"inputs": {},
|
160 |
+
"name": "Import CSV",
|
161 |
+
"outputs": {
|
162 |
+
"output": {
|
163 |
+
"name": "output",
|
164 |
+
"position": "right",
|
165 |
+
"type": {
|
166 |
+
"type": "None"
|
167 |
+
}
|
168 |
+
}
|
169 |
+
},
|
170 |
+
"params": {
|
171 |
+
"columns": {
|
172 |
+
"default": "<from file>",
|
173 |
+
"name": "columns",
|
174 |
+
"type": {
|
175 |
+
"type": "<class 'str'>"
|
176 |
+
}
|
177 |
+
},
|
178 |
+
"filename": {
|
179 |
+
"default": null,
|
180 |
+
"name": "filename",
|
181 |
+
"type": {
|
182 |
+
"type": "<class 'str'>"
|
183 |
+
}
|
184 |
+
},
|
185 |
+
"separator": {
|
186 |
+
"default": "<auto>",
|
187 |
+
"name": "separator",
|
188 |
+
"type": {
|
189 |
+
"type": "<class 'str'>"
|
190 |
+
}
|
191 |
+
}
|
192 |
+
},
|
193 |
+
"type": "basic"
|
194 |
+
},
|
195 |
+
"params": {
|
196 |
+
"columns": "<from file>",
|
197 |
+
"filename": null,
|
198 |
+
"separator": "<auto>"
|
199 |
+
},
|
200 |
+
"status": "done",
|
201 |
+
"title": "Import CSV"
|
202 |
+
},
|
203 |
+
"dragHandle": ".bg-primary",
|
204 |
+
"height": 200.0,
|
205 |
+
"id": "Import CSV 1",
|
206 |
+
"position": {
|
207 |
+
"x": -2.1743215714344757,
|
208 |
+
"y": 346.06014722935214
|
209 |
+
},
|
210 |
+
"type": "basic",
|
211 |
+
"width": 200.0
|
212 |
+
},
|
213 |
+
{
|
214 |
+
"data": {
|
215 |
+
"__execution_delay": 0.0,
|
216 |
+
"collapsed": null,
|
217 |
+
"display": null,
|
218 |
+
"error": "Missing input: bundle",
|
219 |
+
"meta": {
|
220 |
+
"inputs": {
|
221 |
+
"bundle": {
|
222 |
+
"name": "bundle",
|
223 |
+
"position": "left",
|
224 |
+
"type": {
|
225 |
+
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
226 |
+
}
|
227 |
+
}
|
228 |
+
},
|
229 |
+
"name": "Model inference",
|
230 |
+
"outputs": {
|
231 |
+
"output": {
|
232 |
+
"name": "output",
|
233 |
+
"position": "right",
|
234 |
+
"type": {
|
235 |
+
"type": "None"
|
236 |
+
}
|
237 |
+
}
|
238 |
+
},
|
239 |
+
"params": {
|
240 |
+
"model_mapping": {
|
241 |
+
"default": null,
|
242 |
+
"name": "model_mapping",
|
243 |
+
"type": {
|
244 |
+
"type": "<class 'str'>"
|
245 |
+
}
|
246 |
+
},
|
247 |
+
"model_name": {
|
248 |
+
"default": null,
|
249 |
+
"name": "model_name",
|
250 |
+
"type": {
|
251 |
+
"type": "<class 'str'>"
|
252 |
+
}
|
253 |
+
},
|
254 |
+
"save_output_as": {
|
255 |
+
"default": "prediction",
|
256 |
+
"name": "save_output_as",
|
257 |
+
"type": {
|
258 |
+
"type": "<class 'str'>"
|
259 |
+
}
|
260 |
+
}
|
261 |
+
},
|
262 |
+
"type": "basic"
|
263 |
+
},
|
264 |
+
"params": {
|
265 |
+
"model_mapping": "input: data_test",
|
266 |
+
"model_name": "model",
|
267 |
+
"save_output_as": "prediction"
|
268 |
+
},
|
269 |
+
"status": "done",
|
270 |
+
"title": "Model inference"
|
271 |
+
},
|
272 |
+
"dragHandle": ".bg-primary",
|
273 |
+
"height": 339.0,
|
274 |
+
"id": "Model inference 1",
|
275 |
+
"position": {
|
276 |
+
"x": 1736.5697434242886,
|
277 |
+
"y": 357.0743204289906
|
278 |
+
},
|
279 |
+
"type": "basic",
|
280 |
+
"width": 281.0
|
281 |
+
},
|
282 |
+
{
|
283 |
+
"data": {
|
284 |
+
"__execution_delay": null,
|
285 |
+
"collapsed": true,
|
286 |
+
"display": null,
|
287 |
+
"error": "Missing input: bundle",
|
288 |
+
"meta": {
|
289 |
+
"inputs": {
|
290 |
+
"bundle": {
|
291 |
+
"name": "bundle",
|
292 |
+
"position": "left",
|
293 |
+
"type": {
|
294 |
+
"type": "list[lynxkite_graph_analytics.core.Bundle]"
|
295 |
+
}
|
296 |
+
}
|
297 |
+
},
|
298 |
+
"name": "Organize",
|
299 |
+
"outputs": {
|
300 |
+
"output": {
|
301 |
+
"name": "output",
|
302 |
+
"position": "right",
|
303 |
+
"type": {
|
304 |
+
"type": "None"
|
305 |
+
}
|
306 |
+
}
|
307 |
+
},
|
308 |
+
"params": {
|
309 |
+
"relations": {
|
310 |
+
"default": null,
|
311 |
+
"name": "relations",
|
312 |
+
"type": {
|
313 |
+
"type": "<class 'str'>"
|
314 |
+
}
|
315 |
+
}
|
316 |
+
},
|
317 |
+
"type": "graph_creation_view"
|
318 |
+
},
|
319 |
+
"params": {
|
320 |
+
"relations": null
|
321 |
+
},
|
322 |
+
"status": "planned",
|
323 |
+
"title": "Organize"
|
324 |
+
},
|
325 |
+
"dragHandle": ".bg-primary",
|
326 |
+
"height": 322.0,
|
327 |
+
"id": "Create graph 1",
|
328 |
+
"position": {
|
329 |
+
"x": 846.6882598271658,
|
330 |
+
"y": 480.6258932907771
|
331 |
+
},
|
332 |
+
"type": "graph_creation_view",
|
333 |
+
"width": 313.0
|
334 |
+
},
|
335 |
+
{
|
336 |
+
"data": {
|
337 |
+
"__execution_delay": 0.0,
|
338 |
+
"collapsed": null,
|
339 |
+
"display": null,
|
340 |
+
"error": null,
|
341 |
+
"meta": {
|
342 |
+
"inputs": {},
|
343 |
+
"name": "Define model",
|
344 |
+
"outputs": {
|
345 |
+
"output": {
|
346 |
+
"name": "output",
|
347 |
+
"position": "right",
|
348 |
+
"type": {
|
349 |
+
"type": "None"
|
350 |
+
}
|
351 |
+
}
|
352 |
+
},
|
353 |
+
"params": {
|
354 |
+
"model_workspace": {
|
355 |
+
"default": null,
|
356 |
+
"name": "model_workspace",
|
357 |
+
"type": {
|
358 |
+
"type": "<class 'str'>"
|
359 |
+
}
|
360 |
+
},
|
361 |
+
"save_as": {
|
362 |
+
"default": "model",
|
363 |
+
"name": "save_as",
|
364 |
+
"type": {
|
365 |
+
"type": "<class 'str'>"
|
366 |
+
}
|
367 |
+
}
|
368 |
+
},
|
369 |
+
"position": {
|
370 |
+
"x": 286.0,
|
371 |
+
"y": 208.0
|
372 |
+
},
|
373 |
+
"type": "basic"
|
374 |
+
},
|
375 |
+
"params": {
|
376 |
+
"model_workspace": "ODE-GNN",
|
377 |
+
"save_as": "model"
|
378 |
+
},
|
379 |
+
"status": "done",
|
380 |
+
"title": "Define model"
|
381 |
+
},
|
382 |
+
"dragHandle": ".bg-primary",
|
383 |
+
"height": 200.0,
|
384 |
+
"id": "Define model 1",
|
385 |
+
"position": {
|
386 |
+
"x": 311.976524267066,
|
387 |
+
"y": 146.99006795914332
|
388 |
+
},
|
389 |
+
"type": "basic",
|
390 |
+
"width": 200.0
|
391 |
+
},
|
392 |
+
{
|
393 |
+
"data": {
|
394 |
+
"__execution_delay": 0.0,
|
395 |
+
"collapsed": null,
|
396 |
+
"display": null,
|
397 |
+
"error": "Missing input: bundle",
|
398 |
+
"meta": {
|
399 |
+
"inputs": {
|
400 |
+
"bundle": {
|
401 |
+
"name": "bundle",
|
402 |
+
"position": "left",
|
403 |
+
"type": {
|
404 |
+
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
405 |
+
}
|
406 |
+
}
|
407 |
+
},
|
408 |
+
"name": "Train model",
|
409 |
+
"outputs": {
|
410 |
+
"output": {
|
411 |
+
"name": "output",
|
412 |
+
"position": "right",
|
413 |
+
"type": {
|
414 |
+
"type": "None"
|
415 |
+
}
|
416 |
+
}
|
417 |
+
},
|
418 |
+
"params": {
|
419 |
+
"epochs": {
|
420 |
+
"default": 1.0,
|
421 |
+
"name": "epochs",
|
422 |
+
"type": {
|
423 |
+
"type": "<class 'int'>"
|
424 |
+
}
|
425 |
+
},
|
426 |
+
"model_mapping": {
|
427 |
+
"default": null,
|
428 |
+
"name": "model_mapping",
|
429 |
+
"type": {
|
430 |
+
"type": "<class 'str'>"
|
431 |
+
}
|
432 |
+
},
|
433 |
+
"model_name": {
|
434 |
+
"default": null,
|
435 |
+
"name": "model_name",
|
436 |
+
"type": {
|
437 |
+
"type": "<class 'str'>"
|
438 |
+
}
|
439 |
+
}
|
440 |
+
},
|
441 |
+
"position": {
|
442 |
+
"x": 995.0,
|
443 |
+
"y": 350.0
|
444 |
+
},
|
445 |
+
"type": "basic"
|
446 |
+
},
|
447 |
+
"params": {
|
448 |
+
"epochs": 1.0,
|
449 |
+
"model_mapping": "input: data_train",
|
450 |
+
"model_name": "model"
|
451 |
+
},
|
452 |
+
"status": "planned",
|
453 |
+
"title": "Train model"
|
454 |
+
},
|
455 |
+
"dragHandle": ".bg-primary",
|
456 |
+
"height": 342.0,
|
457 |
+
"id": "Train model 1",
|
458 |
+
"position": {
|
459 |
+
"x": 1358.7213662492159,
|
460 |
+
"y": 352.03096133771896
|
461 |
+
},
|
462 |
+
"type": "basic",
|
463 |
+
"width": 296.0
|
464 |
+
}
|
465 |
+
]
|
466 |
+
}
|
examples/ODE-GNN.lynxkite.json
ADDED
@@ -0,0 +1,796 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"edges": [
|
3 |
+
{
|
4 |
+
"id": "Input: embedding 1 Graph conv 1",
|
5 |
+
"source": "Input: embedding 1",
|
6 |
+
"sourceHandle": "x",
|
7 |
+
"target": "Graph conv 1",
|
8 |
+
"targetHandle": "x"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "Input: graph edges 1 Graph conv 1",
|
12 |
+
"source": "Input: graph edges 1",
|
13 |
+
"sourceHandle": "edges",
|
14 |
+
"target": "Graph conv 1",
|
15 |
+
"targetHandle": "edges"
|
16 |
+
},
|
17 |
+
{
|
18 |
+
"id": "Graph conv 1 Activation 1",
|
19 |
+
"source": "Graph conv 1",
|
20 |
+
"sourceHandle": "x",
|
21 |
+
"target": "Activation 1",
|
22 |
+
"targetHandle": "x"
|
23 |
+
},
|
24 |
+
{
|
25 |
+
"id": "Activation 1 Repeat 1",
|
26 |
+
"source": "Activation 1",
|
27 |
+
"sourceHandle": "x",
|
28 |
+
"target": "Repeat 1",
|
29 |
+
"targetHandle": "input"
|
30 |
+
},
|
31 |
+
{
|
32 |
+
"id": "Repeat 1 Graph conv 1",
|
33 |
+
"source": "Repeat 1",
|
34 |
+
"sourceHandle": "output",
|
35 |
+
"target": "Graph conv 1",
|
36 |
+
"targetHandle": "x"
|
37 |
+
},
|
38 |
+
{
|
39 |
+
"id": "Input: sequential 1 LSTM 1",
|
40 |
+
"source": "Input: sequential 1",
|
41 |
+
"sourceHandle": "y",
|
42 |
+
"target": "LSTM 1",
|
43 |
+
"targetHandle": "x"
|
44 |
+
},
|
45 |
+
{
|
46 |
+
"id": "Input: zeros 1 LSTM 1",
|
47 |
+
"source": "Input: zeros 1",
|
48 |
+
"sourceHandle": "x",
|
49 |
+
"target": "LSTM 1",
|
50 |
+
"targetHandle": "h"
|
51 |
+
},
|
52 |
+
{
|
53 |
+
"id": "Recurrent chain 1 LSTM 1",
|
54 |
+
"source": "Recurrent chain 1",
|
55 |
+
"sourceHandle": "output",
|
56 |
+
"target": "LSTM 1",
|
57 |
+
"targetHandle": "h"
|
58 |
+
},
|
59 |
+
{
|
60 |
+
"id": "LSTM 1 Recurrent chain 1",
|
61 |
+
"source": "LSTM 1",
|
62 |
+
"sourceHandle": "h",
|
63 |
+
"target": "Recurrent chain 1",
|
64 |
+
"targetHandle": "input"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"id": "Activation 1 Concatenate 1",
|
68 |
+
"source": "Activation 1",
|
69 |
+
"sourceHandle": "x",
|
70 |
+
"target": "Concatenate 1",
|
71 |
+
"targetHandle": "a"
|
72 |
+
},
|
73 |
+
{
|
74 |
+
"id": "LSTM 1 Concatenate 1",
|
75 |
+
"source": "LSTM 1",
|
76 |
+
"sourceHandle": "x",
|
77 |
+
"target": "Concatenate 1",
|
78 |
+
"targetHandle": "b"
|
79 |
+
},
|
80 |
+
{
|
81 |
+
"id": "Input: label 1 MSE loss 1",
|
82 |
+
"source": "Input: label 1",
|
83 |
+
"sourceHandle": "y",
|
84 |
+
"target": "MSE loss 1",
|
85 |
+
"targetHandle": "y"
|
86 |
+
},
|
87 |
+
{
|
88 |
+
"id": "MSE loss 1 Optimizer 1",
|
89 |
+
"source": "MSE loss 1",
|
90 |
+
"sourceHandle": "loss",
|
91 |
+
"target": "Optimizer 1",
|
92 |
+
"targetHandle": "loss"
|
93 |
+
},
|
94 |
+
{
|
95 |
+
"id": "Concatenate 1 Neural ODE 2",
|
96 |
+
"source": "Concatenate 1",
|
97 |
+
"sourceHandle": "x",
|
98 |
+
"target": "Neural ODE 2",
|
99 |
+
"targetHandle": "x"
|
100 |
+
},
|
101 |
+
{
|
102 |
+
"id": "Neural ODE 2 MSE loss 1",
|
103 |
+
"source": "Neural ODE 2",
|
104 |
+
"sourceHandle": "x",
|
105 |
+
"target": "MSE loss 1",
|
106 |
+
"targetHandle": "x"
|
107 |
+
}
|
108 |
+
],
|
109 |
+
"env": "PyTorch model",
|
110 |
+
"nodes": [
|
111 |
+
{
|
112 |
+
"data": {
|
113 |
+
"display": null,
|
114 |
+
"error": null,
|
115 |
+
"meta": {
|
116 |
+
"inputs": {
|
117 |
+
"edges": {
|
118 |
+
"name": "edges",
|
119 |
+
"position": "bottom",
|
120 |
+
"type": {
|
121 |
+
"type": "tensor"
|
122 |
+
}
|
123 |
+
},
|
124 |
+
"x": {
|
125 |
+
"name": "x",
|
126 |
+
"position": "bottom",
|
127 |
+
"type": {
|
128 |
+
"type": "tensor"
|
129 |
+
}
|
130 |
+
}
|
131 |
+
},
|
132 |
+
"name": "Graph conv",
|
133 |
+
"outputs": {
|
134 |
+
"x": {
|
135 |
+
"name": "x",
|
136 |
+
"position": "top",
|
137 |
+
"type": {
|
138 |
+
"type": "tensor"
|
139 |
+
}
|
140 |
+
}
|
141 |
+
},
|
142 |
+
"params": {
|
143 |
+
"type": {
|
144 |
+
"default": "1",
|
145 |
+
"name": "type",
|
146 |
+
"type": {
|
147 |
+
"enum": [
|
148 |
+
"GCNConv",
|
149 |
+
"GATConv",
|
150 |
+
"GATv2Conv",
|
151 |
+
"SAGEConv"
|
152 |
+
]
|
153 |
+
}
|
154 |
+
}
|
155 |
+
},
|
156 |
+
"type": "basic"
|
157 |
+
},
|
158 |
+
"params": {
|
159 |
+
"type": 1.0
|
160 |
+
},
|
161 |
+
"status": "planned",
|
162 |
+
"title": "Graph conv"
|
163 |
+
},
|
164 |
+
"dragHandle": ".bg-primary",
|
165 |
+
"height": 200.0,
|
166 |
+
"id": "Graph conv 1",
|
167 |
+
"position": {
|
168 |
+
"x": 350.98078368755864,
|
169 |
+
"y": 195.0
|
170 |
+
},
|
171 |
+
"type": "basic",
|
172 |
+
"width": 200.0
|
173 |
+
},
|
174 |
+
{
|
175 |
+
"data": {
|
176 |
+
"__execution_delay": 0.0,
|
177 |
+
"collapsed": null,
|
178 |
+
"display": null,
|
179 |
+
"error": null,
|
180 |
+
"meta": {
|
181 |
+
"inputs": {
|
182 |
+
"input": {
|
183 |
+
"name": "input",
|
184 |
+
"position": "top",
|
185 |
+
"type": {
|
186 |
+
"type": "tensor"
|
187 |
+
}
|
188 |
+
}
|
189 |
+
},
|
190 |
+
"name": "Repeat",
|
191 |
+
"outputs": {
|
192 |
+
"output": {
|
193 |
+
"name": "output",
|
194 |
+
"position": "bottom",
|
195 |
+
"type": {
|
196 |
+
"type": "tensor"
|
197 |
+
}
|
198 |
+
}
|
199 |
+
},
|
200 |
+
"params": {
|
201 |
+
"times": {
|
202 |
+
"default": 1.0,
|
203 |
+
"name": "times",
|
204 |
+
"type": {
|
205 |
+
"type": "<class 'int'>"
|
206 |
+
}
|
207 |
+
}
|
208 |
+
},
|
209 |
+
"type": "basic"
|
210 |
+
},
|
211 |
+
"params": {
|
212 |
+
"times": "5"
|
213 |
+
},
|
214 |
+
"status": "planned",
|
215 |
+
"title": "Repeat"
|
216 |
+
},
|
217 |
+
"dragHandle": ".bg-primary",
|
218 |
+
"height": 200.0,
|
219 |
+
"id": "Repeat 1",
|
220 |
+
"position": {
|
221 |
+
"x": -94.15168677219138,
|
222 |
+
"y": 14.525356969883305
|
223 |
+
},
|
224 |
+
"type": "basic",
|
225 |
+
"width": 200.0
|
226 |
+
},
|
227 |
+
{
|
228 |
+
"data": {
|
229 |
+
"__execution_delay": null,
|
230 |
+
"collapsed": true,
|
231 |
+
"display": null,
|
232 |
+
"error": null,
|
233 |
+
"meta": {
|
234 |
+
"inputs": {
|
235 |
+
"a": {
|
236 |
+
"name": "a",
|
237 |
+
"position": "bottom",
|
238 |
+
"type": {
|
239 |
+
"type": "tensor"
|
240 |
+
}
|
241 |
+
},
|
242 |
+
"b": {
|
243 |
+
"name": "b",
|
244 |
+
"position": "bottom",
|
245 |
+
"type": {
|
246 |
+
"type": "tensor"
|
247 |
+
}
|
248 |
+
}
|
249 |
+
},
|
250 |
+
"name": "Concatenate",
|
251 |
+
"outputs": {
|
252 |
+
"x": {
|
253 |
+
"name": "x",
|
254 |
+
"position": "top",
|
255 |
+
"type": {
|
256 |
+
"type": "tensor"
|
257 |
+
}
|
258 |
+
}
|
259 |
+
},
|
260 |
+
"params": {},
|
261 |
+
"type": "basic"
|
262 |
+
},
|
263 |
+
"params": {},
|
264 |
+
"status": "planned",
|
265 |
+
"title": "Concatenate"
|
266 |
+
},
|
267 |
+
"dragHandle": ".bg-primary",
|
268 |
+
"height": 200.0,
|
269 |
+
"id": "Concatenate 1",
|
270 |
+
"position": {
|
271 |
+
"x": 477.88148637482334,
|
272 |
+
"y": -372.62774030487003
|
273 |
+
},
|
274 |
+
"type": "basic",
|
275 |
+
"width": 200.0
|
276 |
+
},
|
277 |
+
{
|
278 |
+
"data": {
|
279 |
+
"__execution_delay": null,
|
280 |
+
"collapsed": true,
|
281 |
+
"display": null,
|
282 |
+
"error": null,
|
283 |
+
"meta": {
|
284 |
+
"inputs": {},
|
285 |
+
"name": "Input: graph edges",
|
286 |
+
"outputs": {
|
287 |
+
"edges": {
|
288 |
+
"name": "edges",
|
289 |
+
"position": "top",
|
290 |
+
"type": {
|
291 |
+
"type": "tensor"
|
292 |
+
}
|
293 |
+
}
|
294 |
+
},
|
295 |
+
"params": {},
|
296 |
+
"type": "basic"
|
297 |
+
},
|
298 |
+
"params": {},
|
299 |
+
"status": "planned",
|
300 |
+
"title": "Input: graph edges"
|
301 |
+
},
|
302 |
+
"dragHandle": ".bg-primary",
|
303 |
+
"height": 200.0,
|
304 |
+
"id": "Input: graph edges 1",
|
305 |
+
"position": {
|
306 |
+
"x": 515.6535517374441,
|
307 |
+
"y": 545.4709559884296
|
308 |
+
},
|
309 |
+
"type": "basic",
|
310 |
+
"width": 200.0
|
311 |
+
},
|
312 |
+
{
|
313 |
+
"data": {
|
314 |
+
"__execution_delay": null,
|
315 |
+
"collapsed": true,
|
316 |
+
"display": null,
|
317 |
+
"error": null,
|
318 |
+
"meta": {
|
319 |
+
"inputs": {},
|
320 |
+
"name": "Input: embedding",
|
321 |
+
"outputs": {
|
322 |
+
"x": {
|
323 |
+
"name": "x",
|
324 |
+
"position": "top",
|
325 |
+
"type": {
|
326 |
+
"type": "tensor"
|
327 |
+
}
|
328 |
+
}
|
329 |
+
},
|
330 |
+
"params": {},
|
331 |
+
"type": "basic"
|
332 |
+
},
|
333 |
+
"params": {},
|
334 |
+
"status": "planned",
|
335 |
+
"title": "Input: embedding"
|
336 |
+
},
|
337 |
+
"dragHandle": ".bg-primary",
|
338 |
+
"height": 200.0,
|
339 |
+
"id": "Input: embedding 1",
|
340 |
+
"position": {
|
341 |
+
"x": 246.6527948448857,
|
342 |
+
"y": 551.6313504198322
|
343 |
+
},
|
344 |
+
"type": "basic",
|
345 |
+
"width": 200.0
|
346 |
+
},
|
347 |
+
{
|
348 |
+
"data": {
|
349 |
+
"display": null,
|
350 |
+
"error": null,
|
351 |
+
"meta": {
|
352 |
+
"inputs": {
|
353 |
+
"x": {
|
354 |
+
"name": "x",
|
355 |
+
"position": "bottom",
|
356 |
+
"type": {
|
357 |
+
"type": "tensor"
|
358 |
+
}
|
359 |
+
}
|
360 |
+
},
|
361 |
+
"name": "Activation",
|
362 |
+
"outputs": {
|
363 |
+
"x": {
|
364 |
+
"name": "x",
|
365 |
+
"position": "top",
|
366 |
+
"type": {
|
367 |
+
"type": "tensor"
|
368 |
+
}
|
369 |
+
}
|
370 |
+
},
|
371 |
+
"params": {
|
372 |
+
"type": {
|
373 |
+
"default": "1",
|
374 |
+
"name": "type",
|
375 |
+
"type": {
|
376 |
+
"enum": [
|
377 |
+
"ReLU",
|
378 |
+
"LeakyReLU",
|
379 |
+
"Tanh",
|
380 |
+
"Mish"
|
381 |
+
]
|
382 |
+
}
|
383 |
+
}
|
384 |
+
},
|
385 |
+
"type": "basic"
|
386 |
+
},
|
387 |
+
"params": {
|
388 |
+
"type": 1.0
|
389 |
+
},
|
390 |
+
"status": "planned",
|
391 |
+
"title": "Activation"
|
392 |
+
},
|
393 |
+
"dragHandle": ".bg-primary",
|
394 |
+
"height": 200.0,
|
395 |
+
"id": "Activation 1",
|
396 |
+
"position": {
|
397 |
+
"x": 354.3731834561054,
|
398 |
+
"y": -73.74768512965228
|
399 |
+
},
|
400 |
+
"type": "basic",
|
401 |
+
"width": 200.0
|
402 |
+
},
|
403 |
+
{
|
404 |
+
"data": {
|
405 |
+
"__execution_delay": null,
|
406 |
+
"collapsed": true,
|
407 |
+
"display": null,
|
408 |
+
"error": null,
|
409 |
+
"meta": {
|
410 |
+
"inputs": {
|
411 |
+
"h": {
|
412 |
+
"name": "h",
|
413 |
+
"position": "bottom",
|
414 |
+
"type": {
|
415 |
+
"type": "tensor"
|
416 |
+
}
|
417 |
+
},
|
418 |
+
"x": {
|
419 |
+
"name": "x",
|
420 |
+
"position": "bottom",
|
421 |
+
"type": {
|
422 |
+
"type": "tensor"
|
423 |
+
}
|
424 |
+
}
|
425 |
+
},
|
426 |
+
"name": "LSTM",
|
427 |
+
"outputs": {
|
428 |
+
"h": {
|
429 |
+
"name": "h",
|
430 |
+
"position": "top",
|
431 |
+
"type": {
|
432 |
+
"type": "tensor"
|
433 |
+
}
|
434 |
+
},
|
435 |
+
"x": {
|
436 |
+
"name": "x",
|
437 |
+
"position": "top",
|
438 |
+
"type": {
|
439 |
+
"type": "tensor"
|
440 |
+
}
|
441 |
+
}
|
442 |
+
},
|
443 |
+
"params": {},
|
444 |
+
"type": "basic"
|
445 |
+
},
|
446 |
+
"params": {},
|
447 |
+
"status": "planned",
|
448 |
+
"title": "LSTM"
|
449 |
+
},
|
450 |
+
"dragHandle": ".bg-primary",
|
451 |
+
"height": 200.0,
|
452 |
+
"id": "LSTM 1",
|
453 |
+
"position": {
|
454 |
+
"x": 960.0,
|
455 |
+
"y": 135.0
|
456 |
+
},
|
457 |
+
"type": "basic",
|
458 |
+
"width": 200.0
|
459 |
+
},
|
460 |
+
{
|
461 |
+
"data": {
|
462 |
+
"__execution_delay": null,
|
463 |
+
"collapsed": true,
|
464 |
+
"display": null,
|
465 |
+
"error": null,
|
466 |
+
"meta": {
|
467 |
+
"inputs": {},
|
468 |
+
"name": "Input: sequential",
|
469 |
+
"outputs": {
|
470 |
+
"y": {
|
471 |
+
"name": "y",
|
472 |
+
"position": "top",
|
473 |
+
"type": {
|
474 |
+
"type": "tensor"
|
475 |
+
}
|
476 |
+
}
|
477 |
+
},
|
478 |
+
"params": {},
|
479 |
+
"type": "basic"
|
480 |
+
},
|
481 |
+
"params": {},
|
482 |
+
"status": "planned",
|
483 |
+
"title": "Input: sequential"
|
484 |
+
},
|
485 |
+
"dragHandle": ".bg-primary",
|
486 |
+
"height": 200.0,
|
487 |
+
"id": "Input: sequential 1",
|
488 |
+
"position": {
|
489 |
+
"x": 1005.0,
|
490 |
+
"y": 510.0
|
491 |
+
},
|
492 |
+
"type": "basic",
|
493 |
+
"width": 200.0
|
494 |
+
},
|
495 |
+
{
|
496 |
+
"data": {
|
497 |
+
"__execution_delay": null,
|
498 |
+
"collapsed": true,
|
499 |
+
"display": null,
|
500 |
+
"error": null,
|
501 |
+
"meta": {
|
502 |
+
"inputs": {},
|
503 |
+
"name": "Input: zeros",
|
504 |
+
"outputs": {
|
505 |
+
"x": {
|
506 |
+
"name": "x",
|
507 |
+
"position": "top",
|
508 |
+
"type": {
|
509 |
+
"type": "tensor"
|
510 |
+
}
|
511 |
+
}
|
512 |
+
},
|
513 |
+
"params": {},
|
514 |
+
"type": "basic"
|
515 |
+
},
|
516 |
+
"params": {},
|
517 |
+
"status": "planned",
|
518 |
+
"title": "Input: zeros"
|
519 |
+
},
|
520 |
+
"dragHandle": ".bg-primary",
|
521 |
+
"height": 200.0,
|
522 |
+
"id": "Input: zeros 1",
|
523 |
+
"position": {
|
524 |
+
"x": 1290.0,
|
525 |
+
"y": 405.0
|
526 |
+
},
|
527 |
+
"type": "basic",
|
528 |
+
"width": 200.0
|
529 |
+
},
|
530 |
+
{
|
531 |
+
"data": {
|
532 |
+
"__execution_delay": null,
|
533 |
+
"collapsed": true,
|
534 |
+
"display": null,
|
535 |
+
"error": null,
|
536 |
+
"meta": {
|
537 |
+
"inputs": {
|
538 |
+
"input": {
|
539 |
+
"name": "input",
|
540 |
+
"position": "top",
|
541 |
+
"type": {
|
542 |
+
"type": "tensor"
|
543 |
+
}
|
544 |
+
}
|
545 |
+
},
|
546 |
+
"name": "Recurrent chain",
|
547 |
+
"outputs": {
|
548 |
+
"output": {
|
549 |
+
"name": "output",
|
550 |
+
"position": "bottom",
|
551 |
+
"type": {
|
552 |
+
"type": "tensor"
|
553 |
+
}
|
554 |
+
}
|
555 |
+
},
|
556 |
+
"params": {},
|
557 |
+
"type": "basic"
|
558 |
+
},
|
559 |
+
"params": {},
|
560 |
+
"status": "planned",
|
561 |
+
"title": "Recurrent chain"
|
562 |
+
},
|
563 |
+
"dragHandle": ".bg-primary",
|
564 |
+
"height": 200.0,
|
565 |
+
"id": "Recurrent chain 1",
|
566 |
+
"position": {
|
567 |
+
"x": 1224.6603040746108,
|
568 |
+
"y": 135.44839862151363
|
569 |
+
},
|
570 |
+
"type": "basic",
|
571 |
+
"width": 200.0
|
572 |
+
},
|
573 |
+
{
|
574 |
+
"data": {
|
575 |
+
"__execution_delay": null,
|
576 |
+
"collapsed": true,
|
577 |
+
"display": null,
|
578 |
+
"error": null,
|
579 |
+
"meta": {
|
580 |
+
"inputs": {
|
581 |
+
"x": {
|
582 |
+
"name": "x",
|
583 |
+
"position": "bottom",
|
584 |
+
"type": {
|
585 |
+
"type": "tensor"
|
586 |
+
}
|
587 |
+
},
|
588 |
+
"y": {
|
589 |
+
"name": "y",
|
590 |
+
"position": "bottom",
|
591 |
+
"type": {
|
592 |
+
"type": "tensor"
|
593 |
+
}
|
594 |
+
}
|
595 |
+
},
|
596 |
+
"name": "MSE loss",
|
597 |
+
"outputs": {
|
598 |
+
"loss": {
|
599 |
+
"name": "loss",
|
600 |
+
"position": "top",
|
601 |
+
"type": {
|
602 |
+
"type": "tensor"
|
603 |
+
}
|
604 |
+
}
|
605 |
+
},
|
606 |
+
"params": {},
|
607 |
+
"type": "basic"
|
608 |
+
},
|
609 |
+
"params": {},
|
610 |
+
"status": "planned",
|
611 |
+
"title": "MSE loss"
|
612 |
+
},
|
613 |
+
"dragHandle": ".bg-primary",
|
614 |
+
"height": 200.0,
|
615 |
+
"id": "MSE loss 1",
|
616 |
+
"position": {
|
617 |
+
"x": 915.0,
|
618 |
+
"y": -900.0
|
619 |
+
},
|
620 |
+
"type": "basic",
|
621 |
+
"width": 200.0
|
622 |
+
},
|
623 |
+
{
|
624 |
+
"data": {
|
625 |
+
"__execution_delay": null,
|
626 |
+
"collapsed": true,
|
627 |
+
"display": null,
|
628 |
+
"error": null,
|
629 |
+
"meta": {
|
630 |
+
"inputs": {},
|
631 |
+
"name": "Input: label",
|
632 |
+
"outputs": {
|
633 |
+
"y": {
|
634 |
+
"name": "y",
|
635 |
+
"position": "top",
|
636 |
+
"type": {
|
637 |
+
"type": "tensor"
|
638 |
+
}
|
639 |
+
}
|
640 |
+
},
|
641 |
+
"params": {},
|
642 |
+
"type": "basic"
|
643 |
+
},
|
644 |
+
"params": {},
|
645 |
+
"status": "planned",
|
646 |
+
"title": "Input: label"
|
647 |
+
},
|
648 |
+
"dragHandle": ".bg-primary",
|
649 |
+
"height": 200.0,
|
650 |
+
"id": "Input: label 1",
|
651 |
+
"position": {
|
652 |
+
"x": 1095.0,
|
653 |
+
"y": -450.0
|
654 |
+
},
|
655 |
+
"type": "basic",
|
656 |
+
"width": 200.0
|
657 |
+
},
|
658 |
+
{
|
659 |
+
"data": {
|
660 |
+
"display": null,
|
661 |
+
"error": null,
|
662 |
+
"meta": {
|
663 |
+
"inputs": {
|
664 |
+
"loss": {
|
665 |
+
"name": "loss",
|
666 |
+
"position": "bottom",
|
667 |
+
"type": {
|
668 |
+
"type": "tensor"
|
669 |
+
}
|
670 |
+
}
|
671 |
+
},
|
672 |
+
"name": "Optimizer",
|
673 |
+
"outputs": {},
|
674 |
+
"params": {
|
675 |
+
"lr": {
|
676 |
+
"default": 0.001,
|
677 |
+
"name": "lr",
|
678 |
+
"type": {
|
679 |
+
"type": "<class 'float'>"
|
680 |
+
}
|
681 |
+
},
|
682 |
+
"type": {
|
683 |
+
"default": "1",
|
684 |
+
"name": "type",
|
685 |
+
"type": {
|
686 |
+
"enum": [
|
687 |
+
"AdamW",
|
688 |
+
"Adafactor",
|
689 |
+
"Adagrad",
|
690 |
+
"SGD",
|
691 |
+
"Lion",
|
692 |
+
"Paged AdamW",
|
693 |
+
"Galore AdamW"
|
694 |
+
]
|
695 |
+
}
|
696 |
+
}
|
697 |
+
},
|
698 |
+
"type": "basic"
|
699 |
+
},
|
700 |
+
"params": {
|
701 |
+
"lr": 0.001,
|
702 |
+
"type": 1.0
|
703 |
+
},
|
704 |
+
"status": "planned",
|
705 |
+
"title": "Optimizer"
|
706 |
+
},
|
707 |
+
"dragHandle": ".bg-primary",
|
708 |
+
"height": 247.0,
|
709 |
+
"id": "Optimizer 1",
|
710 |
+
"position": {
|
711 |
+
"x": 915.3430278730226,
|
712 |
+
"y": -1268.0577550022126
|
713 |
+
},
|
714 |
+
"type": "basic",
|
715 |
+
"width": 190.0
|
716 |
+
},
|
717 |
+
{
|
718 |
+
"data": {
|
719 |
+
"display": null,
|
720 |
+
"error": null,
|
721 |
+
"meta": {
|
722 |
+
"inputs": {
|
723 |
+
"x": {
|
724 |
+
"name": "x",
|
725 |
+
"position": "bottom",
|
726 |
+
"type": {
|
727 |
+
"type": "tensor"
|
728 |
+
}
|
729 |
+
}
|
730 |
+
},
|
731 |
+
"name": "Neural ODE",
|
732 |
+
"outputs": {
|
733 |
+
"x": {
|
734 |
+
"name": "x",
|
735 |
+
"position": "top",
|
736 |
+
"type": {
|
737 |
+
"type": "tensor"
|
738 |
+
}
|
739 |
+
}
|
740 |
+
},
|
741 |
+
"params": {
|
742 |
+
"absolute_tolerance": {
|
743 |
+
"default": null,
|
744 |
+
"name": "absolute_tolerance",
|
745 |
+
"type": {
|
746 |
+
"type": "None"
|
747 |
+
}
|
748 |
+
},
|
749 |
+
"method": {
|
750 |
+
"default": "1",
|
751 |
+
"name": "method",
|
752 |
+
"type": {
|
753 |
+
"enum": [
|
754 |
+
"dopri8",
|
755 |
+
"dopri5",
|
756 |
+
"bosh3",
|
757 |
+
"fehlberg2",
|
758 |
+
"adaptive_heun",
|
759 |
+
"euler",
|
760 |
+
"midpoint",
|
761 |
+
"rk4",
|
762 |
+
"explicit_adams",
|
763 |
+
"implicit_adams"
|
764 |
+
]
|
765 |
+
}
|
766 |
+
},
|
767 |
+
"relative_tolerance": {
|
768 |
+
"default": null,
|
769 |
+
"name": "relative_tolerance",
|
770 |
+
"type": {
|
771 |
+
"type": "None"
|
772 |
+
}
|
773 |
+
}
|
774 |
+
},
|
775 |
+
"type": "basic"
|
776 |
+
},
|
777 |
+
"params": {
|
778 |
+
"absolute_tolerance": null,
|
779 |
+
"method": 1.0,
|
780 |
+
"relative_tolerance": null
|
781 |
+
},
|
782 |
+
"status": "planned",
|
783 |
+
"title": "Neural ODE"
|
784 |
+
},
|
785 |
+
"dragHandle": ".bg-primary",
|
786 |
+
"height": 200.0,
|
787 |
+
"id": "Neural ODE 2",
|
788 |
+
"position": {
|
789 |
+
"x": 342.3226409443945,
|
790 |
+
"y": -687.1882072175634
|
791 |
+
},
|
792 |
+
"type": "basic",
|
793 |
+
"width": 200.0
|
794 |
+
}
|
795 |
+
]
|
796 |
+
}
|
examples/{PyTorch demo → PyTorch demo.lynxkite.json}
RENAMED
File without changes
|
examples/{RAG chatbot app → RAG chatbot app.lynxkite.json}
RENAMED
File without changes
|
examples/Word2vec.lynxkite.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
examples/fake_data.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from lynxkite.core.ops import op
|
2 |
+
from faker import Faker
|
3 |
+
import pandas as pd
|
4 |
+
|
5 |
+
faker = Faker()
|
6 |
+
|
7 |
+
|
8 |
+
@op("LynxKite Graph Analytics", "Fake data")
|
9 |
+
def fake(*, n=10):
|
10 |
+
df = pd.DataFrame(
|
11 |
+
{
|
12 |
+
"name": [faker.name() for _ in range(n)],
|
13 |
+
"address": [faker.address() for _ in range(n)],
|
14 |
+
}
|
15 |
+
)
|
16 |
+
return df
|
examples/requirements.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
Faker
|
examples/{sql → sql.lynxkite.json}
RENAMED
File without changes
|
examples/uploads/plus-one-dataset.parquet
ADDED
Binary file (7.54 kB). View file
|
|
examples/word2vec.py
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from lynxkite.core.ops import op
|
2 |
+
import staticvectors
|
3 |
+
import pandas as pd
|
4 |
+
|
5 |
+
ENV = "LynxKite Graph Analytics"
|
6 |
+
|
7 |
+
|
8 |
+
@op(ENV, "Word2vec for the top 1000 words", slow=True)
|
9 |
+
def word2vec_1000():
|
10 |
+
model = staticvectors.StaticVectors("neuml/word2vec-quantized")
|
11 |
+
df = pd.read_csv(
|
12 |
+
"https://gist.githubusercontent.com/deekayen/4148741/raw/98d35708fa344717d8eee15d11987de6c8e26d7d/1-1000.txt",
|
13 |
+
names=["word"],
|
14 |
+
)
|
15 |
+
df["embedding"] = model.embeddings(df.word.tolist()).tolist()
|
16 |
+
return df
|
17 |
+
|
18 |
+
|
19 |
+
@op(ENV, "Take first N")
|
20 |
+
def first_n(df: pd.DataFrame, *, n=10):
|
21 |
+
return df.head(n)
|
22 |
+
|
23 |
+
|
24 |
+
@op(ENV, "Sample N")
|
25 |
+
def sample_n(df: pd.DataFrame, *, n=10):
|
26 |
+
return df.sample(n)
|
lynxkite-app/src/lynxkite_app/crdt.py
CHANGED
@@ -26,11 +26,11 @@ def ws_exception_handler(exception, log):
|
|
26 |
return True
|
27 |
|
28 |
|
29 |
-
class
|
30 |
async def init_room(self, name: str) -> pycrdt_websocket.YRoom:
|
31 |
"""Initialize a room for the workspace with the given name.
|
32 |
|
33 |
-
The workspace is loaded from "
|
34 |
"""
|
35 |
crdt_path = pathlib.Path(".crdt")
|
36 |
path = crdt_path / f"{name}.crdt"
|
@@ -40,9 +40,7 @@ class WebsocketServer(pycrdt_websocket.WebsocketServer):
|
|
40 |
ydoc["workspace"] = ws = pycrdt.Map()
|
41 |
# Replay updates from the store.
|
42 |
try:
|
43 |
-
for update, timestamp in [
|
44 |
-
(item[0], item[-1]) async for item in ystore.read()
|
45 |
-
]:
|
46 |
ydoc.apply_update(update)
|
47 |
except pycrdt_websocket.ystore.YDocNotFound:
|
48 |
pass
|
@@ -84,14 +82,49 @@ class WebsocketServer(pycrdt_websocket.WebsocketServer):
|
|
84 |
return room
|
85 |
|
86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
last_ws_input = None
|
88 |
|
89 |
|
90 |
def clean_input(ws_pyd):
|
91 |
for node in ws_pyd.nodes:
|
92 |
node.data.display = None
|
|
|
93 |
node.data.error = None
|
94 |
node.data.status = workspace.NodeStatus.done
|
|
|
|
|
|
|
95 |
node.position.x = 0
|
96 |
node.position.y = 0
|
97 |
if node.model_extra:
|
@@ -168,9 +201,12 @@ def try_to_load_workspace(ws: pycrdt.Map, name: str):
|
|
168 |
"""
|
169 |
if os.path.exists(name):
|
170 |
ws_pyd = workspace.load(name)
|
171 |
-
|
172 |
-
|
173 |
-
|
|
|
|
|
|
|
174 |
|
175 |
|
176 |
last_known_versions = {}
|
@@ -208,9 +244,7 @@ async def workspace_changed(name: str, changes: pycrdt.MapEvent, ws_crdt: pycrdt
|
|
208 |
await execute(name, ws_crdt, ws_pyd)
|
209 |
|
210 |
|
211 |
-
async def execute(
|
212 |
-
name: str, ws_crdt: pycrdt.Map, ws_pyd: workspace.Workspace, delay: int = 0
|
213 |
-
):
|
214 |
"""Execute the workspace and update the CRDT object with the results.
|
215 |
|
216 |
Args:
|
@@ -230,6 +264,7 @@ async def execute(
|
|
230 |
assert path.is_relative_to(cwd), "Provided workspace path is invalid"
|
231 |
# Save user changes before executing, in case the execution fails.
|
232 |
workspace.save(ws_pyd, path)
|
|
|
233 |
ws_pyd._crdt = ws_crdt
|
234 |
with ws_crdt.doc.transaction():
|
235 |
for nc, np in zip(ws_crdt["nodes"], ws_pyd.nodes):
|
@@ -243,14 +278,21 @@ async def execute(
|
|
243 |
print(f"Finished running {name} in {ws_pyd.env}.")
|
244 |
|
245 |
|
|
|
|
|
|
|
|
|
|
|
|
|
246 |
@contextlib.asynccontextmanager
|
247 |
async def lifespan(app):
|
248 |
-
global
|
249 |
-
|
250 |
-
|
251 |
-
)
|
252 |
-
async with
|
253 |
-
|
|
|
254 |
print("closing websocket server")
|
255 |
|
256 |
|
@@ -261,5 +303,12 @@ def sanitize_path(path):
|
|
261 |
@router.websocket("/ws/crdt/{room_name}")
|
262 |
async def crdt_websocket(websocket: fastapi.WebSocket, room_name: str):
|
263 |
room_name = sanitize_path(room_name)
|
264 |
-
server = pycrdt_websocket.ASGIServer(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
265 |
await server({"path": room_name}, websocket._receive, websocket._send)
|
|
|
26 |
return True
|
27 |
|
28 |
|
29 |
+
class WorkspaceWebsocketServer(pycrdt_websocket.WebsocketServer):
|
30 |
async def init_room(self, name: str) -> pycrdt_websocket.YRoom:
|
31 |
"""Initialize a room for the workspace with the given name.
|
32 |
|
33 |
+
The workspace is loaded from ".crdt" if it exists there, or from a JSON file, or a new workspace is created.
|
34 |
"""
|
35 |
crdt_path = pathlib.Path(".crdt")
|
36 |
path = crdt_path / f"{name}.crdt"
|
|
|
40 |
ydoc["workspace"] = ws = pycrdt.Map()
|
41 |
# Replay updates from the store.
|
42 |
try:
|
43 |
+
for update, timestamp in [(item[0], item[-1]) async for item in ystore.read()]:
|
|
|
|
|
44 |
ydoc.apply_update(update)
|
45 |
except pycrdt_websocket.ystore.YDocNotFound:
|
46 |
pass
|
|
|
82 |
return room
|
83 |
|
84 |
|
85 |
+
class CodeWebsocketServer(WorkspaceWebsocketServer):
|
86 |
+
async def init_room(self, name: str) -> pycrdt_websocket.YRoom:
|
87 |
+
"""Initialize a room for a text document with the given name."""
|
88 |
+
crdt_path = pathlib.Path(".crdt")
|
89 |
+
path = crdt_path / f"{name}.crdt"
|
90 |
+
assert path.is_relative_to(crdt_path)
|
91 |
+
ystore = pycrdt_websocket.ystore.FileYStore(path)
|
92 |
+
ydoc = pycrdt.Doc()
|
93 |
+
ydoc["text"] = text = pycrdt.Text()
|
94 |
+
# Replay updates from the store.
|
95 |
+
try:
|
96 |
+
for update, timestamp in [(item[0], item[-1]) async for item in ystore.read()]:
|
97 |
+
ydoc.apply_update(update)
|
98 |
+
except pycrdt_websocket.ystore.YDocNotFound:
|
99 |
+
pass
|
100 |
+
if len(text) == 0:
|
101 |
+
if os.path.exists(name):
|
102 |
+
with open(name) as f:
|
103 |
+
text += f.read()
|
104 |
+
room = pycrdt_websocket.YRoom(
|
105 |
+
ystore=ystore, ydoc=ydoc, exception_handler=ws_exception_handler
|
106 |
+
)
|
107 |
+
room.text = text
|
108 |
+
|
109 |
+
def on_change(changes):
|
110 |
+
asyncio.create_task(code_changed(name, changes, text))
|
111 |
+
|
112 |
+
text.observe(on_change)
|
113 |
+
return room
|
114 |
+
|
115 |
+
|
116 |
last_ws_input = None
|
117 |
|
118 |
|
119 |
def clean_input(ws_pyd):
|
120 |
for node in ws_pyd.nodes:
|
121 |
node.data.display = None
|
122 |
+
node.data.input_metadata = None
|
123 |
node.data.error = None
|
124 |
node.data.status = workspace.NodeStatus.done
|
125 |
+
for p in list(node.data.params):
|
126 |
+
if p.startswith("_"):
|
127 |
+
del node.data.params[p]
|
128 |
node.position.x = 0
|
129 |
node.position.y = 0
|
130 |
if node.model_extra:
|
|
|
201 |
"""
|
202 |
if os.path.exists(name):
|
203 |
ws_pyd = workspace.load(name)
|
204 |
+
crdt_update(
|
205 |
+
ws,
|
206 |
+
ws_pyd.model_dump(),
|
207 |
+
# We treat some fields as black boxes. They are not edited on the frontend.
|
208 |
+
non_collaborative_fields={"display", "input_metadata"},
|
209 |
+
)
|
210 |
|
211 |
|
212 |
last_known_versions = {}
|
|
|
244 |
await execute(name, ws_crdt, ws_pyd)
|
245 |
|
246 |
|
247 |
+
async def execute(name: str, ws_crdt: pycrdt.Map, ws_pyd: workspace.Workspace, delay: int = 0):
|
|
|
|
|
248 |
"""Execute the workspace and update the CRDT object with the results.
|
249 |
|
250 |
Args:
|
|
|
264 |
assert path.is_relative_to(cwd), "Provided workspace path is invalid"
|
265 |
# Save user changes before executing, in case the execution fails.
|
266 |
workspace.save(ws_pyd, path)
|
267 |
+
ops.load_user_scripts(name)
|
268 |
ws_pyd._crdt = ws_crdt
|
269 |
with ws_crdt.doc.transaction():
|
270 |
for nc, np in zip(ws_crdt["nodes"], ws_pyd.nodes):
|
|
|
278 |
print(f"Finished running {name} in {ws_pyd.env}.")
|
279 |
|
280 |
|
281 |
+
async def code_changed(name: str, changes: pycrdt.TextEvent, text: pycrdt.Text):
|
282 |
+
# TODO: Make this more fancy?
|
283 |
+
with open(name, "w") as f:
|
284 |
+
f.write(str(text).strip() + "\n")
|
285 |
+
|
286 |
+
|
287 |
@contextlib.asynccontextmanager
|
288 |
async def lifespan(app):
|
289 |
+
global ws_websocket_server
|
290 |
+
global code_websocket_server
|
291 |
+
ws_websocket_server = WorkspaceWebsocketServer(auto_clean_rooms=False)
|
292 |
+
code_websocket_server = CodeWebsocketServer(auto_clean_rooms=False)
|
293 |
+
async with ws_websocket_server:
|
294 |
+
async with code_websocket_server:
|
295 |
+
yield
|
296 |
print("closing websocket server")
|
297 |
|
298 |
|
|
|
303 |
@router.websocket("/ws/crdt/{room_name}")
|
304 |
async def crdt_websocket(websocket: fastapi.WebSocket, room_name: str):
|
305 |
room_name = sanitize_path(room_name)
|
306 |
+
server = pycrdt_websocket.ASGIServer(ws_websocket_server)
|
307 |
+
await server({"path": room_name}, websocket._receive, websocket._send)
|
308 |
+
|
309 |
+
|
310 |
+
@router.websocket("/ws/code/crdt/{room_name}")
|
311 |
+
async def code_crdt_websocket(websocket: fastapi.WebSocket, room_name: str):
|
312 |
+
room_name = sanitize_path(room_name)
|
313 |
+
server = pycrdt_websocket.ASGIServer(code_websocket_server)
|
314 |
await server({"path": room_name}, websocket._receive, websocket._send)
|
lynxkite-app/src/lynxkite_app/main.py
CHANGED
@@ -26,6 +26,7 @@ def detect_plugins():
|
|
26 |
|
27 |
|
28 |
lynxkite_plugins = detect_plugins()
|
|
|
29 |
|
30 |
app = fastapi.FastAPI(lifespan=crdt.lifespan)
|
31 |
app.include_router(crdt.router)
|
@@ -33,11 +34,9 @@ app.add_middleware(GZipMiddleware)
|
|
33 |
|
34 |
|
35 |
@app.get("/api/catalog")
|
36 |
-
def get_catalog():
|
37 |
-
|
38 |
-
|
39 |
-
for k, v in ops.CATALOGS.items()
|
40 |
-
}
|
41 |
|
42 |
|
43 |
class SaveRequest(workspace.BaseConfig):
|
@@ -85,6 +84,15 @@ class DirectoryEntry(pydantic.BaseModel):
|
|
85 |
type: str
|
86 |
|
87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
@app.get("/api/dir/list")
|
89 |
def list_dir(path: str):
|
90 |
path = data_path / path
|
@@ -93,7 +101,7 @@ def list_dir(path: str):
|
|
93 |
[
|
94 |
DirectoryEntry(
|
95 |
name=str(p.relative_to(data_path)),
|
96 |
-
type=
|
97 |
)
|
98 |
for p in path.iterdir()
|
99 |
if not p.name.startswith(".")
|
|
|
26 |
|
27 |
|
28 |
lynxkite_plugins = detect_plugins()
|
29 |
+
ops.save_catalogs("plugins loaded")
|
30 |
|
31 |
app = fastapi.FastAPI(lifespan=crdt.lifespan)
|
32 |
app.include_router(crdt.router)
|
|
|
34 |
|
35 |
|
36 |
@app.get("/api/catalog")
|
37 |
+
def get_catalog(workspace: str):
|
38 |
+
ops.load_user_scripts(workspace)
|
39 |
+
return {k: {op.name: op.model_dump() for op in v.values()} for k, v in ops.CATALOGS.items()}
|
|
|
|
|
40 |
|
41 |
|
42 |
class SaveRequest(workspace.BaseConfig):
|
|
|
84 |
type: str
|
85 |
|
86 |
|
87 |
+
def _get_path_type(path: pathlib.Path) -> str:
|
88 |
+
if path.is_dir():
|
89 |
+
return "directory"
|
90 |
+
elif path.suffixes[-2:] == [".lynxkite", ".json"]:
|
91 |
+
return "workspace"
|
92 |
+
else:
|
93 |
+
return "file"
|
94 |
+
|
95 |
+
|
96 |
@app.get("/api/dir/list")
|
97 |
def list_dir(path: str):
|
98 |
path = data_path / path
|
|
|
101 |
[
|
102 |
DirectoryEntry(
|
103 |
name=str(p.relative_to(data_path)),
|
104 |
+
type=_get_path_type(p),
|
105 |
)
|
106 |
for p in path.iterdir()
|
107 |
if not p.name.startswith(".")
|
lynxkite-app/tests/test_main.py
CHANGED
@@ -22,7 +22,7 @@ def test_detect_plugins_with_plugins():
|
|
22 |
|
23 |
|
24 |
def test_get_catalog():
|
25 |
-
response = client.get("/api/catalog")
|
26 |
assert response.status_code == 200
|
27 |
|
28 |
|
@@ -37,6 +37,7 @@ def test_save_and_load():
|
|
37 |
"type": "basic",
|
38 |
"data": {
|
39 |
"display": None,
|
|
|
40 |
"error": "Unknown operation.",
|
41 |
"title": "Test node",
|
42 |
"params": {"param1": "value"},
|
@@ -58,15 +59,22 @@ def test_save_and_load():
|
|
58 |
def test_list_dir():
|
59 |
test_dir = pathlib.Path() / str(uuid.uuid4())
|
60 |
test_dir.mkdir(parents=True, exist_ok=True)
|
61 |
-
|
62 |
-
|
|
|
|
|
|
|
|
|
63 |
response = client.get(f"/api/dir/list?path={str(test_dir)}")
|
64 |
assert response.status_code == 200
|
65 |
-
assert
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
|
|
|
|
|
|
70 |
|
71 |
|
72 |
def test_make_dir():
|
|
|
22 |
|
23 |
|
24 |
def test_get_catalog():
|
25 |
+
response = client.get("/api/catalog?workspace=test")
|
26 |
assert response.status_code == 200
|
27 |
|
28 |
|
|
|
37 |
"type": "basic",
|
38 |
"data": {
|
39 |
"display": None,
|
40 |
+
"input_metadata": None,
|
41 |
"error": "Unknown operation.",
|
42 |
"title": "Test node",
|
43 |
"params": {"param1": "value"},
|
|
|
59 |
def test_list_dir():
|
60 |
test_dir = pathlib.Path() / str(uuid.uuid4())
|
61 |
test_dir.mkdir(parents=True, exist_ok=True)
|
62 |
+
dir = test_dir / "test_dir"
|
63 |
+
dir.mkdir(exist_ok=True)
|
64 |
+
file = test_dir / "test_file.txt"
|
65 |
+
file.touch()
|
66 |
+
ws = test_dir / "test_workspace.lynxkite.json"
|
67 |
+
ws.touch()
|
68 |
response = client.get(f"/api/dir/list?path={str(test_dir)}")
|
69 |
assert response.status_code == 200
|
70 |
+
assert response.json() == [
|
71 |
+
{"name": f"{test_dir}/test_dir", "type": "directory"},
|
72 |
+
{"name": f"{test_dir}/test_file.txt", "type": "file"},
|
73 |
+
{"name": f"{test_dir}/test_workspace.lynxkite.json", "type": "workspace"},
|
74 |
+
]
|
75 |
+
file.unlink()
|
76 |
+
ws.unlink()
|
77 |
+
dir.rmdir()
|
78 |
|
79 |
|
80 |
def test_make_dir():
|
lynxkite-app/web/eslint.config.js
CHANGED
@@ -19,10 +19,7 @@ export default tseslint.config(
|
|
19 |
},
|
20 |
rules: {
|
21 |
...reactHooks.configs.recommended.rules,
|
22 |
-
"react-refresh/only-export-components": [
|
23 |
-
"warn",
|
24 |
-
{ allowConstantExport: true },
|
25 |
-
],
|
26 |
},
|
27 |
},
|
28 |
);
|
|
|
19 |
},
|
20 |
rules: {
|
21 |
...reactHooks.configs.recommended.rules,
|
22 |
+
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
|
|
|
|
|
|
|
23 |
},
|
24 |
},
|
25 |
);
|
lynxkite-app/web/index.html
CHANGED
@@ -4,7 +4,6 @@
|
|
4 |
<meta charset="UTF-8" />
|
5 |
<link rel="icon" type="image/svg+xml" href="/src/assets/favicon.ico" />
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
-
<title>LynxKite 2025</title>
|
8 |
</head>
|
9 |
<body>
|
10 |
<div id="root"></div>
|
|
|
4 |
<meta charset="UTF-8" />
|
5 |
<link rel="icon" type="image/svg+xml" href="/src/assets/favicon.ico" />
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
|
7 |
</head>
|
8 |
<body>
|
9 |
<div id="root"></div>
|
lynxkite-app/web/package-lock.json
CHANGED
@@ -10,6 +10,7 @@
|
|
10 |
"dependencies": {
|
11 |
"@esbuild/linux-x64": "^0.25.0",
|
12 |
"@iconify-json/tabler": "^1.2.10",
|
|
|
13 |
"@svgr/core": "^8.1.0",
|
14 |
"@svgr/plugin-jsx": "^8.1.0",
|
15 |
"@swc/core": "^1.10.1",
|
@@ -28,6 +29,7 @@
|
|
28 |
"react-router-dom": "^7.0.2",
|
29 |
"swr": "^2.2.5",
|
30 |
"unplugin-icons": "^0.21.0",
|
|
|
31 |
"y-websocket": "^2.0.4",
|
32 |
"yjs": "^13.6.20"
|
33 |
},
|
@@ -1105,6 +1107,29 @@
|
|
1105 |
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
|
1106 |
"license": "MIT"
|
1107 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1108 |
"node_modules/@nodelib/fs.scandir": {
|
1109 |
"version": "2.1.5",
|
1110 |
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
@@ -5436,6 +5461,13 @@
|
|
5436 |
"ufo": "^1.5.4"
|
5437 |
}
|
5438 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5439 |
"node_modules/ms": {
|
5440 |
"version": "2.1.3",
|
5441 |
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
@@ -6437,6 +6469,12 @@
|
|
6437 |
"url": "https://github.com/sponsors/wooorm"
|
6438 |
}
|
6439 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
6440 |
"node_modules/string_decoder": {
|
6441 |
"version": "1.3.0",
|
6442 |
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
@@ -7360,6 +7398,23 @@
|
|
7360 |
"yjs": "^13.0.0"
|
7361 |
}
|
7362 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7363 |
"node_modules/y-protocols": {
|
7364 |
"version": "1.0.6",
|
7365 |
"resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz",
|
|
|
10 |
"dependencies": {
|
11 |
"@esbuild/linux-x64": "^0.25.0",
|
12 |
"@iconify-json/tabler": "^1.2.10",
|
13 |
+
"@monaco-editor/react": "^4.7.0",
|
14 |
"@svgr/core": "^8.1.0",
|
15 |
"@svgr/plugin-jsx": "^8.1.0",
|
16 |
"@swc/core": "^1.10.1",
|
|
|
29 |
"react-router-dom": "^7.0.2",
|
30 |
"swr": "^2.2.5",
|
31 |
"unplugin-icons": "^0.21.0",
|
32 |
+
"y-monaco": "^0.1.6",
|
33 |
"y-websocket": "^2.0.4",
|
34 |
"yjs": "^13.6.20"
|
35 |
},
|
|
|
1107 |
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
|
1108 |
"license": "MIT"
|
1109 |
},
|
1110 |
+
"node_modules/@monaco-editor/loader": {
|
1111 |
+
"version": "1.5.0",
|
1112 |
+
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.5.0.tgz",
|
1113 |
+
"integrity": "sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==",
|
1114 |
+
"license": "MIT",
|
1115 |
+
"dependencies": {
|
1116 |
+
"state-local": "^1.0.6"
|
1117 |
+
}
|
1118 |
+
},
|
1119 |
+
"node_modules/@monaco-editor/react": {
|
1120 |
+
"version": "4.7.0",
|
1121 |
+
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
|
1122 |
+
"integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
|
1123 |
+
"license": "MIT",
|
1124 |
+
"dependencies": {
|
1125 |
+
"@monaco-editor/loader": "^1.5.0"
|
1126 |
+
},
|
1127 |
+
"peerDependencies": {
|
1128 |
+
"monaco-editor": ">= 0.25.0 < 1",
|
1129 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
1130 |
+
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
1131 |
+
}
|
1132 |
+
},
|
1133 |
"node_modules/@nodelib/fs.scandir": {
|
1134 |
"version": "2.1.5",
|
1135 |
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
|
|
5461 |
"ufo": "^1.5.4"
|
5462 |
}
|
5463 |
},
|
5464 |
+
"node_modules/monaco-editor": {
|
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",
|
5473 |
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
|
|
6469 |
"url": "https://github.com/sponsors/wooorm"
|
6470 |
}
|
6471 |
},
|
6472 |
+
"node_modules/state-local": {
|
6473 |
+
"version": "1.0.7",
|
6474 |
+
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
6475 |
+
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
|
6476 |
+
"license": "MIT"
|
6477 |
+
},
|
6478 |
"node_modules/string_decoder": {
|
6479 |
"version": "1.3.0",
|
6480 |
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
|
|
7398 |
"yjs": "^13.0.0"
|
7399 |
}
|
7400 |
},
|
7401 |
+
"node_modules/y-monaco": {
|
7402 |
+
"version": "0.1.6",
|
7403 |
+
"resolved": "https://registry.npmjs.org/y-monaco/-/y-monaco-0.1.6.tgz",
|
7404 |
+
"integrity": "sha512-sYRywMmcylt+Nupl+11AvizD2am06ST8lkVbUXuaEmrtV6Tf+TD4rsEm6u9YGGowYue+Vfg1IJ97SUP2J+PVXg==",
|
7405 |
+
"license": "MIT",
|
7406 |
+
"dependencies": {
|
7407 |
+
"lib0": "^0.2.43"
|
7408 |
+
},
|
7409 |
+
"engines": {
|
7410 |
+
"node": ">=12.0.0",
|
7411 |
+
"npm": ">=6.0.0"
|
7412 |
+
},
|
7413 |
+
"peerDependencies": {
|
7414 |
+
"monaco-editor": ">=0.20.0",
|
7415 |
+
"yjs": "^13.3.1"
|
7416 |
+
}
|
7417 |
+
},
|
7418 |
"node_modules/y-protocols": {
|
7419 |
"version": "1.0.6",
|
7420 |
"resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz",
|
lynxkite-app/web/package.json
CHANGED
@@ -13,6 +13,7 @@
|
|
13 |
"dependencies": {
|
14 |
"@esbuild/linux-x64": "^0.25.0",
|
15 |
"@iconify-json/tabler": "^1.2.10",
|
|
|
16 |
"@svgr/core": "^8.1.0",
|
17 |
"@svgr/plugin-jsx": "^8.1.0",
|
18 |
"@swc/core": "^1.10.1",
|
@@ -31,6 +32,7 @@
|
|
31 |
"react-router-dom": "^7.0.2",
|
32 |
"swr": "^2.2.5",
|
33 |
"unplugin-icons": "^0.21.0",
|
|
|
34 |
"y-websocket": "^2.0.4",
|
35 |
"yjs": "^13.6.20"
|
36 |
},
|
|
|
13 |
"dependencies": {
|
14 |
"@esbuild/linux-x64": "^0.25.0",
|
15 |
"@iconify-json/tabler": "^1.2.10",
|
16 |
+
"@monaco-editor/react": "^4.7.0",
|
17 |
"@svgr/core": "^8.1.0",
|
18 |
"@svgr/plugin-jsx": "^8.1.0",
|
19 |
"@swc/core": "^1.10.1",
|
|
|
32 |
"react-router-dom": "^7.0.2",
|
33 |
"swr": "^2.2.5",
|
34 |
"unplugin-icons": "^0.21.0",
|
35 |
+
"y-monaco": "^0.1.6",
|
36 |
"y-websocket": "^2.0.4",
|
37 |
"yjs": "^13.6.20"
|
38 |
},
|
lynxkite-app/web/playwright.config.ts
CHANGED
@@ -7,6 +7,7 @@ export default defineConfig({
|
|
7 |
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
8 |
forbidOnly: !!process.env.CI,
|
9 |
retries: process.env.CI ? 1 : 0,
|
|
|
10 |
workers: 1,
|
11 |
reporter: process.env.CI ? [["github"], ["html"]] : "html",
|
12 |
use: {
|
@@ -24,7 +25,7 @@ export default defineConfig({
|
|
24 |
],
|
25 |
webServer: {
|
26 |
command: "cd ../../examples && lynxkite",
|
27 |
-
|
28 |
reuseExistingServer: false,
|
29 |
},
|
30 |
});
|
|
|
7 |
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
8 |
forbidOnly: !!process.env.CI,
|
9 |
retries: process.env.CI ? 1 : 0,
|
10 |
+
maxFailures: 5,
|
11 |
workers: 1,
|
12 |
reporter: process.env.CI ? [["github"], ["html"]] : "html",
|
13 |
use: {
|
|
|
25 |
],
|
26 |
webServer: {
|
27 |
command: "cd ../../examples && lynxkite",
|
28 |
+
port: 8000,
|
29 |
reuseExistingServer: false,
|
30 |
},
|
31 |
});
|
lynxkite-app/web/src/Code.tsx
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Full-page editor for code files.
|
2 |
+
|
3 |
+
import Editor, { type Monaco } from "@monaco-editor/react";
|
4 |
+
import type { editor } from "monaco-editor";
|
5 |
+
import { useEffect, useRef } from "react";
|
6 |
+
import { useParams } from "react-router";
|
7 |
+
import { WebsocketProvider } from "y-websocket";
|
8 |
+
import * as Y from "yjs";
|
9 |
+
// @ts-ignore
|
10 |
+
import Atom from "~icons/tabler/atom.jsx";
|
11 |
+
// @ts-ignore
|
12 |
+
import Backspace from "~icons/tabler/backspace.jsx";
|
13 |
+
// @ts-ignore
|
14 |
+
import Close from "~icons/tabler/x.jsx";
|
15 |
+
import favicon from "./assets/favicon.ico";
|
16 |
+
import theme from "./code-theme.ts";
|
17 |
+
|
18 |
+
export default function Code() {
|
19 |
+
const { path } = useParams();
|
20 |
+
const parentDir = path!.split("/").slice(0, -1).join("/");
|
21 |
+
const yDocRef = useRef<any>();
|
22 |
+
const wsProviderRef = useRef<any>();
|
23 |
+
const monacoBindingRef = useRef<any>();
|
24 |
+
const yMonacoRef = useRef<any>();
|
25 |
+
const editorRef = useRef<any>();
|
26 |
+
useEffect(() => {
|
27 |
+
const loadMonaco = async () => {
|
28 |
+
// y-monaco is gigantic. The other Monaco packages are small.
|
29 |
+
yMonacoRef.current = await import("y-monaco");
|
30 |
+
initCRDT();
|
31 |
+
};
|
32 |
+
loadMonaco();
|
33 |
+
}, []);
|
34 |
+
function beforeMount(monaco: Monaco) {
|
35 |
+
monaco.editor.defineTheme("lynxkite", theme);
|
36 |
+
}
|
37 |
+
function onMount(_editor: editor.IStandaloneCodeEditor) {
|
38 |
+
editorRef.current = _editor;
|
39 |
+
initCRDT();
|
40 |
+
}
|
41 |
+
function initCRDT() {
|
42 |
+
if (!yMonacoRef.current || !editorRef.current) return;
|
43 |
+
if (yDocRef.current) return;
|
44 |
+
yDocRef.current = new Y.Doc();
|
45 |
+
const text = yDocRef.current.getText("text");
|
46 |
+
const proto = location.protocol === "https:" ? "wss:" : "ws:";
|
47 |
+
wsProviderRef.current = new WebsocketProvider(
|
48 |
+
`${proto}//${location.host}/ws/code/crdt`,
|
49 |
+
path!,
|
50 |
+
yDocRef.current,
|
51 |
+
);
|
52 |
+
monacoBindingRef.current = new yMonacoRef.current.MonacoBinding(
|
53 |
+
text,
|
54 |
+
editorRef.current.getModel()!,
|
55 |
+
new Set([editorRef.current]),
|
56 |
+
wsProviderRef.current.awareness,
|
57 |
+
);
|
58 |
+
}
|
59 |
+
useEffect(() => {
|
60 |
+
return () => {
|
61 |
+
yDocRef.current?.destroy();
|
62 |
+
wsProviderRef.current?.destroy();
|
63 |
+
monacoBindingRef.current?.destroy();
|
64 |
+
};
|
65 |
+
});
|
66 |
+
return (
|
67 |
+
<div className="workspace">
|
68 |
+
<div className="top-bar bg-neutral">
|
69 |
+
<a className="logo" href="">
|
70 |
+
<img alt="" src={favicon} />
|
71 |
+
</a>
|
72 |
+
<div className="ws-name">{path}</div>
|
73 |
+
<div className="tools text-secondary">
|
74 |
+
<a href="">
|
75 |
+
<Atom />
|
76 |
+
</a>
|
77 |
+
<a href="">
|
78 |
+
<Backspace />
|
79 |
+
</a>
|
80 |
+
<a href={`/dir/${parentDir}`}>
|
81 |
+
<Close />
|
82 |
+
</a>
|
83 |
+
</div>
|
84 |
+
</div>
|
85 |
+
<Editor
|
86 |
+
defaultLanguage="python"
|
87 |
+
theme="lynxkite"
|
88 |
+
path={path}
|
89 |
+
beforeMount={beforeMount}
|
90 |
+
onMount={onMount}
|
91 |
+
loading={null}
|
92 |
+
options={{
|
93 |
+
cursorStyle: "block",
|
94 |
+
cursorBlinking: "solid",
|
95 |
+
minimap: { enabled: false },
|
96 |
+
renderLineHighlight: "none",
|
97 |
+
}}
|
98 |
+
/>
|
99 |
+
</div>
|
100 |
+
);
|
101 |
+
}
|
lynxkite-app/web/src/Directory.tsx
CHANGED
@@ -15,9 +15,46 @@ import FolderPlus from "~icons/tabler/folder-plus";
|
|
15 |
// @ts-ignore
|
16 |
import Home from "~icons/tabler/home";
|
17 |
// @ts-ignore
|
|
|
|
|
|
|
|
|
18 |
import Trash from "~icons/tabler/trash";
|
19 |
import logo from "./assets/logo.png";
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
22 |
|
23 |
export default function () {
|
@@ -27,68 +64,51 @@ export default function () {
|
|
27 |
dedupingInterval: 0,
|
28 |
});
|
29 |
const navigate = useNavigate();
|
30 |
-
const [isCreatingDir, setIsCreatingDir] = useState(false);
|
31 |
-
const [isCreatingWorkspace, setIsCreatingWorkspace] = useState(false);
|
32 |
|
33 |
function link(item: DirectoryEntry) {
|
34 |
if (item.type === "directory") {
|
35 |
return `/dir/${item.name}`;
|
36 |
}
|
37 |
-
|
|
|
|
|
|
|
38 |
}
|
39 |
|
40 |
function shortName(item: DirectoryEntry) {
|
41 |
-
return item.name
|
|
|
|
|
|
|
42 |
}
|
43 |
|
44 |
-
function
|
45 |
-
|
46 |
-
|
47 |
-
const name = `${baseName}${i ? ` ${i}` : ""}`;
|
48 |
-
if (!list.find((item) => item.name === name)) {
|
49 |
-
return name;
|
50 |
-
}
|
51 |
-
i++;
|
52 |
-
}
|
53 |
}
|
54 |
-
|
55 |
-
function newWorkspaceIn(
|
56 |
-
path: string,
|
57 |
-
list: DirectoryEntry[],
|
58 |
-
workspaceName?: string,
|
59 |
-
) {
|
60 |
const pathSlash = path ? `${path}/` : "";
|
61 |
-
|
62 |
-
navigate(`/edit/${pathSlash}${name}`, { replace: true });
|
63 |
}
|
64 |
-
|
65 |
-
async function newFolderIn(
|
66 |
-
path: string,
|
67 |
-
list: DirectoryEntry[],
|
68 |
-
folderName?: string,
|
69 |
-
) {
|
70 |
-
const name = folderName || newName(list, "New Folder");
|
71 |
const pathSlash = path ? `${path}/` : "";
|
72 |
-
|
73 |
const res = await fetch("/api/dir/mkdir", {
|
74 |
method: "POST",
|
75 |
headers: { "Content-Type": "application/json" },
|
76 |
-
body: JSON.stringify({ path: pathSlash +
|
77 |
});
|
78 |
if (res.ok) {
|
79 |
-
navigate(`/dir/${pathSlash}${
|
80 |
} else {
|
81 |
alert("Failed to create folder.");
|
82 |
}
|
83 |
}
|
84 |
|
85 |
async function deleteItem(item: DirectoryEntry) {
|
86 |
-
if (!window.confirm(`Are you sure you want to delete "${item.name}"?`))
|
87 |
-
return;
|
88 |
const pathSlash = path ? `${path}/` : "";
|
89 |
|
90 |
-
const apiPath =
|
91 |
-
item.type === "directory" ? "/api/dir/delete" : "/api/delete";
|
92 |
await fetch(apiPath, {
|
93 |
method: "POST",
|
94 |
headers: { "Content-Type": "application/json" },
|
@@ -115,89 +135,66 @@ export default function () {
|
|
115 |
{list.data && (
|
116 |
<>
|
117 |
<div className="actions">
|
118 |
-
<
|
119 |
-
{
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
</form>
|
140 |
-
)}
|
141 |
-
<button
|
142 |
-
type="button"
|
143 |
-
onClick={() => setIsCreatingWorkspace(true)}
|
144 |
-
>
|
145 |
-
<FolderPlus /> New workspace
|
146 |
-
</button>
|
147 |
-
</div>
|
148 |
-
|
149 |
-
<div className="new-folder">
|
150 |
-
{isCreatingDir && (
|
151 |
-
// @ts-ignore
|
152 |
-
<form
|
153 |
-
onSubmit={(e) => {
|
154 |
-
e.preventDefault();
|
155 |
-
newFolderIn(
|
156 |
-
path || "",
|
157 |
-
list.data,
|
158 |
-
(e.target as HTMLFormElement).folderName.value.trim(),
|
159 |
-
);
|
160 |
-
}}
|
161 |
-
>
|
162 |
-
<input
|
163 |
-
type="text"
|
164 |
-
name="folderName"
|
165 |
-
defaultValue={newName(list.data)}
|
166 |
-
placeholder={newName(list.data)}
|
167 |
-
/>
|
168 |
-
</form>
|
169 |
-
)}
|
170 |
-
<button type="button" onClick={() => setIsCreatingDir(true)}>
|
171 |
-
<FolderPlus /> New folder
|
172 |
-
</button>
|
173 |
-
</div>
|
174 |
</div>
|
175 |
|
176 |
-
{path
|
177 |
<div className="breadcrumbs">
|
178 |
<Link to="/dir/">
|
179 |
<Home />
|
180 |
</Link>{" "}
|
181 |
<span className="current-folder">{path}</span>
|
|
|
182 |
</div>
|
|
|
|
|
183 |
)}
|
184 |
|
185 |
-
{list.data.map(
|
186 |
-
|
187 |
-
|
188 |
-
{item.
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
</>
|
202 |
)}
|
203 |
</div>{" "}
|
|
|
15 |
// @ts-ignore
|
16 |
import Home from "~icons/tabler/home";
|
17 |
// @ts-ignore
|
18 |
+
import LayoutGrid from "~icons/tabler/layout-grid";
|
19 |
+
// @ts-ignore
|
20 |
+
import LayoutGridAdd from "~icons/tabler/layout-grid-add";
|
21 |
+
// @ts-ignore
|
22 |
import Trash from "~icons/tabler/trash";
|
23 |
import logo from "./assets/logo.png";
|
24 |
|
25 |
+
function EntryCreator(props: {
|
26 |
+
label: string;
|
27 |
+
icon: JSX.Element;
|
28 |
+
onCreate: (name: string) => void;
|
29 |
+
}) {
|
30 |
+
const [isCreating, setIsCreating] = useState(false);
|
31 |
+
return (
|
32 |
+
<>
|
33 |
+
{isCreating ? (
|
34 |
+
<form
|
35 |
+
onSubmit={(e) => {
|
36 |
+
e.preventDefault();
|
37 |
+
props.onCreate((e.target as HTMLFormElement).entryName.value.trim());
|
38 |
+
}}
|
39 |
+
>
|
40 |
+
<input
|
41 |
+
className="input input-ghost w-full"
|
42 |
+
autoFocus
|
43 |
+
type="text"
|
44 |
+
name="entryName"
|
45 |
+
onBlur={() => setIsCreating(false)}
|
46 |
+
placeholder={`${props.label} name`}
|
47 |
+
/>
|
48 |
+
</form>
|
49 |
+
) : (
|
50 |
+
<button type="button" onClick={() => setIsCreating(true)}>
|
51 |
+
{props.icon} {props.label}
|
52 |
+
</button>
|
53 |
+
)}
|
54 |
+
</>
|
55 |
+
);
|
56 |
+
}
|
57 |
+
|
58 |
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
59 |
|
60 |
export default function () {
|
|
|
64 |
dedupingInterval: 0,
|
65 |
});
|
66 |
const navigate = useNavigate();
|
|
|
|
|
67 |
|
68 |
function link(item: DirectoryEntry) {
|
69 |
if (item.type === "directory") {
|
70 |
return `/dir/${item.name}`;
|
71 |
}
|
72 |
+
if (item.type === "workspace") {
|
73 |
+
return `/edit/${item.name}`;
|
74 |
+
}
|
75 |
+
return `/code/${item.name}`;
|
76 |
}
|
77 |
|
78 |
function shortName(item: DirectoryEntry) {
|
79 |
+
return item.name
|
80 |
+
.split("/")
|
81 |
+
.pop()
|
82 |
+
?.replace(/[.]lynxkite[.]json$/, "");
|
83 |
}
|
84 |
|
85 |
+
function newWorkspaceIn(path: string, workspaceName: string) {
|
86 |
+
const pathSlash = path ? `${path}/` : "";
|
87 |
+
navigate(`/edit/${pathSlash}${workspaceName}.lynxkite.json`, { replace: true });
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
}
|
89 |
+
function newCodeFile(path: string, name: string) {
|
|
|
|
|
|
|
|
|
|
|
90 |
const pathSlash = path ? `${path}/` : "";
|
91 |
+
navigate(`/code/${pathSlash}${name}`, { replace: true });
|
|
|
92 |
}
|
93 |
+
async function newFolderIn(path: string, folderName: string) {
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
const pathSlash = path ? `${path}/` : "";
|
|
|
95 |
const res = await fetch("/api/dir/mkdir", {
|
96 |
method: "POST",
|
97 |
headers: { "Content-Type": "application/json" },
|
98 |
+
body: JSON.stringify({ path: pathSlash + folderName }),
|
99 |
});
|
100 |
if (res.ok) {
|
101 |
+
navigate(`/dir/${pathSlash}${folderName}`);
|
102 |
} else {
|
103 |
alert("Failed to create folder.");
|
104 |
}
|
105 |
}
|
106 |
|
107 |
async function deleteItem(item: DirectoryEntry) {
|
108 |
+
if (!window.confirm(`Are you sure you want to delete "${item.name}"?`)) return;
|
|
|
109 |
const pathSlash = path ? `${path}/` : "";
|
110 |
|
111 |
+
const apiPath = item.type === "directory" ? "/api/dir/delete" : "/api/delete";
|
|
|
112 |
await fetch(apiPath, {
|
113 |
method: "POST",
|
114 |
headers: { "Content-Type": "application/json" },
|
|
|
135 |
{list.data && (
|
136 |
<>
|
137 |
<div className="actions">
|
138 |
+
<EntryCreator
|
139 |
+
onCreate={(name) => {
|
140 |
+
newWorkspaceIn(path || "", name);
|
141 |
+
}}
|
142 |
+
icon={<LayoutGridAdd />}
|
143 |
+
label="New workspace"
|
144 |
+
/>
|
145 |
+
<EntryCreator
|
146 |
+
onCreate={(name) => {
|
147 |
+
newCodeFile(path || "", name);
|
148 |
+
}}
|
149 |
+
icon={<FilePlus />}
|
150 |
+
label="New code file"
|
151 |
+
/>
|
152 |
+
<EntryCreator
|
153 |
+
onCreate={(name: string) => {
|
154 |
+
newFolderIn(path || "", name);
|
155 |
+
}}
|
156 |
+
icon={<FolderPlus />}
|
157 |
+
label="New folder"
|
158 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
</div>
|
160 |
|
161 |
+
{path ? (
|
162 |
<div className="breadcrumbs">
|
163 |
<Link to="/dir/">
|
164 |
<Home />
|
165 |
</Link>{" "}
|
166 |
<span className="current-folder">{path}</span>
|
167 |
+
<title>{path}</title>
|
168 |
</div>
|
169 |
+
) : (
|
170 |
+
<title>LynxKite 2000:MM</title>
|
171 |
)}
|
172 |
|
173 |
+
{list.data.map(
|
174 |
+
(item: DirectoryEntry) =>
|
175 |
+
!shortName(item)?.startsWith("__") && (
|
176 |
+
<div key={item.name} className="entry">
|
177 |
+
<Link key={link(item)} to={link(item)}>
|
178 |
+
{item.type === "directory" ? (
|
179 |
+
<Folder />
|
180 |
+
) : item.type === "workspace" ? (
|
181 |
+
<LayoutGrid />
|
182 |
+
) : (
|
183 |
+
<File />
|
184 |
+
)}
|
185 |
+
{shortName(item)}
|
186 |
+
</Link>
|
187 |
+
<button
|
188 |
+
type="button"
|
189 |
+
onClick={() => {
|
190 |
+
deleteItem(item);
|
191 |
+
}}
|
192 |
+
>
|
193 |
+
<Trash />
|
194 |
+
</button>
|
195 |
+
</div>
|
196 |
+
),
|
197 |
+
)}
|
198 |
</>
|
199 |
)}
|
200 |
</div>{" "}
|
lynxkite-app/web/src/apiTypes.ts
CHANGED
@@ -5,6 +5,8 @@
|
|
5 |
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
6 |
*/
|
7 |
|
|
|
|
|
8 |
export interface DirectoryEntry {
|
9 |
name: string;
|
10 |
type: string;
|
@@ -40,8 +42,9 @@ export interface WorkspaceNodeData {
|
|
40 |
[k: string]: unknown;
|
41 |
};
|
42 |
display?: unknown;
|
|
|
43 |
error?: string | null;
|
44 |
-
|
45 |
[k: string]: unknown;
|
46 |
}
|
47 |
export interface Position {
|
|
|
5 |
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
6 |
*/
|
7 |
|
8 |
+
export type NodeStatus = "planned" | "active" | "done";
|
9 |
+
|
10 |
export interface DirectoryEntry {
|
11 |
name: string;
|
12 |
type: string;
|
|
|
42 |
[k: string]: unknown;
|
43 |
};
|
44 |
display?: unknown;
|
45 |
+
input_metadata?: unknown;
|
46 |
error?: string | null;
|
47 |
+
status?: NodeStatus;
|
48 |
[k: string]: unknown;
|
49 |
}
|
50 |
export interface Position {
|
lynxkite-app/web/src/code-theme.ts
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// A simple theme using the LynxKite colors.
|
2 |
+
|
3 |
+
import type { editor } from "monaco-editor/esm/vs/editor/editor.api";
|
4 |
+
|
5 |
+
const theme: editor.IStandaloneThemeData = {
|
6 |
+
base: "vs-dark",
|
7 |
+
inherit: true,
|
8 |
+
rules: [
|
9 |
+
{
|
10 |
+
foreground: "ff8800",
|
11 |
+
token: "keyword",
|
12 |
+
},
|
13 |
+
{
|
14 |
+
foreground: "0088ff",
|
15 |
+
fontStyle: "italic",
|
16 |
+
token: "comment",
|
17 |
+
},
|
18 |
+
{
|
19 |
+
foreground: "39bcf3",
|
20 |
+
token: "string",
|
21 |
+
},
|
22 |
+
{
|
23 |
+
foreground: "ffc600",
|
24 |
+
token: "",
|
25 |
+
},
|
26 |
+
],
|
27 |
+
colors: {
|
28 |
+
"editor.foreground": "#FFFFFF",
|
29 |
+
"editor.background": "#002a4c",
|
30 |
+
"editor.selectionBackground": "#0050a4",
|
31 |
+
"editor.lineHighlightBackground": "#1f4662",
|
32 |
+
"editorCursor.foreground": "#ffc600",
|
33 |
+
"editorWhitespace.foreground": "#7f7f7fb2",
|
34 |
+
"editorIndentGuide.background": "#3b5364",
|
35 |
+
"editorIndentGuide.activeBackground": "#ffc600",
|
36 |
+
},
|
37 |
+
};
|
38 |
+
export default theme;
|
lynxkite-app/web/src/index.css
CHANGED
@@ -102,15 +102,14 @@ body {
|
|
102 |
--status-color-1: oklch(75% 0.2 55);
|
103 |
--status-color-2: oklch(75% 0.2 55);
|
104 |
--status-color-3: oklch(75% 0.2 55);
|
105 |
-
transition: --status-color-1 0.3s, --status-color-2 0.3s, --status-color-3
|
106 |
-
0.3s;
|
107 |
}
|
108 |
|
109 |
.lynxkite-node .title.active {
|
110 |
--status-color-1: oklch(75% 0.2 55);
|
111 |
--status-color-2: oklch(90% 0.2 55);
|
112 |
--status-color-3: oklch(75% 0.1 55);
|
113 |
-
|
114 |
}
|
115 |
|
116 |
.lynxkite-node .title.planned {
|
@@ -256,6 +255,14 @@ body {
|
|
256 |
cursor: pointer;
|
257 |
}
|
258 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
259 |
}
|
260 |
|
261 |
.params-expander {
|
@@ -319,7 +326,14 @@ body {
|
|
319 |
.actions {
|
320 |
display: flex;
|
321 |
justify-content: space-evenly;
|
|
|
|
|
322 |
padding: 5px;
|
|
|
|
|
|
|
|
|
|
|
323 |
}
|
324 |
|
325 |
.actions a {
|
@@ -527,3 +541,25 @@ body {
|
|
527 |
.add-relationship-button:hover {
|
528 |
background-color: #218838;
|
529 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
--status-color-1: oklch(75% 0.2 55);
|
103 |
--status-color-2: oklch(75% 0.2 55);
|
104 |
--status-color-3: oklch(75% 0.2 55);
|
105 |
+
transition: --status-color-1 0.3s, --status-color-2 0.3s, --status-color-3 0.3s;
|
|
|
106 |
}
|
107 |
|
108 |
.lynxkite-node .title.active {
|
109 |
--status-color-1: oklch(75% 0.2 55);
|
110 |
--status-color-2: oklch(90% 0.2 55);
|
111 |
--status-color-3: oklch(75% 0.1 55);
|
112 |
+
animation: active-node-gradient-animation 2s ease-in-out infinite;
|
113 |
}
|
114 |
|
115 |
.lynxkite-node .title.planned {
|
|
|
255 |
cursor: pointer;
|
256 |
}
|
257 |
}
|
258 |
+
|
259 |
+
.model-mapping-param {
|
260 |
+
border: 1px solid var(--fallback-bc, oklch(var(--bc) / 0.2));
|
261 |
+
border-collapse: separate;
|
262 |
+
border-radius: 5px;
|
263 |
+
padding: 5px 10px;
|
264 |
+
width: 100%;
|
265 |
+
}
|
266 |
}
|
267 |
|
268 |
.params-expander {
|
|
|
326 |
.actions {
|
327 |
display: flex;
|
328 |
justify-content: space-evenly;
|
329 |
+
align-items: center;
|
330 |
+
height: 50px;
|
331 |
padding: 5px;
|
332 |
+
|
333 |
+
form,
|
334 |
+
button {
|
335 |
+
flex: 1;
|
336 |
+
}
|
337 |
}
|
338 |
|
339 |
.actions a {
|
|
|
541 |
.add-relationship-button:hover {
|
542 |
background-color: #218838;
|
543 |
}
|
544 |
+
|
545 |
+
.yRemoteSelection {
|
546 |
+
background-color: rgb(250, 129, 0, 0.5);
|
547 |
+
}
|
548 |
+
|
549 |
+
.yRemoteSelectionHead {
|
550 |
+
position: absolute;
|
551 |
+
border-left: #ff8800 solid 2px;
|
552 |
+
border-top: #ff8800 solid 2px;
|
553 |
+
border-bottom: #ff8800 solid 2px;
|
554 |
+
height: 100%;
|
555 |
+
box-sizing: border-box;
|
556 |
+
}
|
557 |
+
|
558 |
+
.yRemoteSelectionHead::after {
|
559 |
+
position: absolute;
|
560 |
+
content: " ";
|
561 |
+
border: 3px solid #ff8800;
|
562 |
+
border-radius: 4px;
|
563 |
+
left: -4px;
|
564 |
+
top: -5px;
|
565 |
+
}
|
lynxkite-app/web/src/main.tsx
CHANGED
@@ -3,6 +3,7 @@ import { createRoot } from "react-dom/client";
|
|
3 |
import "@xyflow/react/dist/style.css";
|
4 |
import "./index.css";
|
5 |
import { BrowserRouter, Route, Routes } from "react-router";
|
|
|
6 |
import Directory from "./Directory.tsx";
|
7 |
import Workspace from "./workspace/Workspace.tsx";
|
8 |
|
@@ -14,6 +15,7 @@ createRoot(document.getElementById("root")!).render(
|
|
14 |
<Route path="/dir" element={<Directory />} />
|
15 |
<Route path="/dir/:path" element={<Directory />} />
|
16 |
<Route path="/edit/:path" element={<Workspace />} />
|
|
|
17 |
</Routes>
|
18 |
</BrowserRouter>
|
19 |
</StrictMode>,
|
|
|
3 |
import "@xyflow/react/dist/style.css";
|
4 |
import "./index.css";
|
5 |
import { BrowserRouter, Route, Routes } from "react-router";
|
6 |
+
import Code from "./Code.tsx";
|
7 |
import Directory from "./Directory.tsx";
|
8 |
import Workspace from "./workspace/Workspace.tsx";
|
9 |
|
|
|
15 |
<Route path="/dir" element={<Directory />} />
|
16 |
<Route path="/dir/:path" element={<Directory />} />
|
17 |
<Route path="/edit/:path" element={<Workspace />} />
|
18 |
+
<Route path="/code/:path" element={<Code />} />
|
19 |
</Routes>
|
20 |
</BrowserRouter>
|
21 |
</StrictMode>,
|
lynxkite-app/web/src/workspace/NodeSearch.tsx
CHANGED
@@ -30,9 +30,7 @@ export default function (props: {
|
|
30 |
boxes.sort((a, b) => a.item.name.localeCompare(b.item.name));
|
31 |
return boxes;
|
32 |
}, [props.boxes]);
|
33 |
-
const hits: { item: OpsOp }[] = searchText
|
34 |
-
? fuse.search<OpsOp>(searchText)
|
35 |
-
: allOps;
|
36 |
const [selectedIndex, setSelectedIndex] = useState(0);
|
37 |
useEffect(() => searchBox.current.focus());
|
38 |
function typed(text: string) {
|
@@ -64,10 +62,7 @@ export default function (props: {
|
|
64 |
}
|
65 |
|
66 |
return (
|
67 |
-
<div
|
68 |
-
className="node-search"
|
69 |
-
style={{ top: props.pos.y, left: props.pos.x }}
|
70 |
-
>
|
71 |
<input
|
72 |
ref={searchBox}
|
73 |
value={searchText}
|
|
|
30 |
boxes.sort((a, b) => a.item.name.localeCompare(b.item.name));
|
31 |
return boxes;
|
32 |
}, [props.boxes]);
|
33 |
+
const hits: { item: OpsOp }[] = searchText ? fuse.search<OpsOp>(searchText) : allOps;
|
|
|
|
|
34 |
const [selectedIndex, setSelectedIndex] = useState(0);
|
35 |
useEffect(() => searchBox.current.focus());
|
36 |
function typed(text: string) {
|
|
|
62 |
}
|
63 |
|
64 |
return (
|
65 |
+
<div className="node-search" style={{ top: props.pos.y, left: props.pos.x }}>
|
|
|
|
|
|
|
66 |
<input
|
67 |
ref={searchBox}
|
68 |
value={searchText}
|
lynxkite-app/web/src/workspace/Workspace.tsx
CHANGED
@@ -1,3 +1,5 @@
|
|
|
|
|
|
1 |
import { getYjsDoc, syncedStore } from "@syncedstore/core";
|
2 |
import {
|
3 |
type Connection,
|
@@ -15,14 +17,7 @@ import {
|
|
15 |
useUpdateNodeInternals,
|
16 |
} from "@xyflow/react";
|
17 |
import axios from "axios";
|
18 |
-
import {
|
19 |
-
type MouseEvent,
|
20 |
-
useCallback,
|
21 |
-
useEffect,
|
22 |
-
useMemo,
|
23 |
-
useState,
|
24 |
-
} from "react";
|
25 |
-
// The LynxKite workspace editor.
|
26 |
import { useParams } from "react-router";
|
27 |
import useSWR, { type Fetcher } from "swr";
|
28 |
import { WebsocketProvider } from "y-websocket";
|
@@ -37,11 +32,7 @@ import favicon from "../assets/favicon.ico";
|
|
37 |
// import NodeWithTableView from './NodeWithTableView';
|
38 |
import EnvironmentSelector from "./EnvironmentSelector";
|
39 |
import { LynxKiteState } from "./LynxKiteState";
|
40 |
-
import NodeSearch, {
|
41 |
-
type OpsOp,
|
42 |
-
type Catalog,
|
43 |
-
type Catalogs,
|
44 |
-
} from "./NodeSearch.tsx";
|
45 |
import NodeWithGraphCreationView from "./nodes/GraphCreationNode.tsx";
|
46 |
import NodeWithImage from "./nodes/NodeWithImage.tsx";
|
47 |
import NodeWithParams from "./nodes/NodeWithParams";
|
@@ -62,6 +53,10 @@ function LynxKiteFlow() {
|
|
62 |
const [nodes, setNodes] = useState([] as Node[]);
|
63 |
const [edges, setEdges] = useState([] as Edge[]);
|
64 |
const { path } = useParams();
|
|
|
|
|
|
|
|
|
65 |
const [state, setState] = useState({ workspace: {} as Workspace });
|
66 |
const [message, setMessage] = useState(null as string | null);
|
67 |
useEffect(() => {
|
@@ -69,11 +64,7 @@ function LynxKiteFlow() {
|
|
69 |
setState(state);
|
70 |
const doc = getYjsDoc(state);
|
71 |
const proto = location.protocol === "https:" ? "wss:" : "ws:";
|
72 |
-
const wsProvider = new WebsocketProvider(
|
73 |
-
`${proto}//${location.host}/ws/crdt`,
|
74 |
-
path!,
|
75 |
-
doc,
|
76 |
-
);
|
77 |
const onChange = (_update: any, origin: any, _doc: any, _tr: any) => {
|
78 |
if (origin === wsProvider) {
|
79 |
// An update from the CRDT. Apply it to the local state.
|
@@ -165,7 +156,7 @@ function LynxKiteFlow() {
|
|
165 |
|
166 |
const fetcher: Fetcher<Catalogs> = (resource: string, init?: RequestInit) =>
|
167 |
fetch(resource, init).then((res) => res.json());
|
168 |
-
const catalog = useSWR(
|
169 |
const [suppressSearchUntil, setSuppressSearchUntil] = useState(0);
|
170 |
const [nodeSearchSettings, setNodeSearchSettings] = useState(
|
171 |
undefined as
|
@@ -190,11 +181,7 @@ function LynxKiteFlow() {
|
|
190 |
useEffect(() => {
|
191 |
const handleKeyDown = (event: KeyboardEvent) => {
|
192 |
// Show the node search dialog on "/".
|
193 |
-
if (
|
194 |
-
event.key === "/" &&
|
195 |
-
!nodeSearchSettings &&
|
196 |
-
!isTypingInFormElement()
|
197 |
-
) {
|
198 |
event.preventDefault();
|
199 |
setNodeSearchSettings({
|
200 |
pos: { x: 100, y: 100 },
|
@@ -238,11 +225,7 @@ function LynxKiteFlow() {
|
|
238 |
},
|
239 |
[catalog, state, nodeSearchSettings, suppressSearchUntil, closeNodeSearch],
|
240 |
);
|
241 |
-
function addNode(
|
242 |
-
node: Partial<WorkspaceNode>,
|
243 |
-
state: { workspace: Workspace },
|
244 |
-
nodes: Node[],
|
245 |
-
) {
|
246 |
const title = node.data?.title;
|
247 |
let i = 1;
|
248 |
node.id = `${title} ${i}`;
|
@@ -260,9 +243,7 @@ function LynxKiteFlow() {
|
|
260 |
data: {
|
261 |
meta: meta,
|
262 |
title: meta.name,
|
263 |
-
params: Object.fromEntries(
|
264 |
-
Object.values(meta.params).map((p) => [p.name, p.default]),
|
265 |
-
),
|
266 |
},
|
267 |
};
|
268 |
return node;
|
@@ -310,9 +291,7 @@ function LynxKiteFlow() {
|
|
310 |
try {
|
311 |
await axios.post("/api/upload", formData, {
|
312 |
onUploadProgress: (progressEvent) => {
|
313 |
-
const percentCompleted = Math.round(
|
314 |
-
(100 * progressEvent.loaded) / progressEvent.total!,
|
315 |
-
);
|
316 |
if (percentCompleted === 100) setMessage("Processing file...");
|
317 |
else setMessage(`Uploading ${percentCompleted}%`);
|
318 |
},
|
@@ -345,7 +324,8 @@ function LynxKiteFlow() {
|
|
345 |
<a className="logo" href="">
|
346 |
<img alt="" src={favicon} />
|
347 |
</a>
|
348 |
-
<div className="ws-name">{
|
|
|
349 |
<EnvironmentSelector
|
350 |
options={Object.keys(catalog.data || {})}
|
351 |
value={state.workspace.env!}
|
@@ -365,11 +345,7 @@ function LynxKiteFlow() {
|
|
365 |
</a>
|
366 |
</div>
|
367 |
</div>
|
368 |
-
<div
|
369 |
-
style={{ height: "100%", width: "100vw" }}
|
370 |
-
onDragOver={onDragOver}
|
371 |
-
onDrop={onDrop}
|
372 |
-
>
|
373 |
<LynxKiteState.Provider value={state}>
|
374 |
<ReactFlow
|
375 |
nodes={nodes}
|
@@ -382,7 +358,9 @@ function LynxKiteFlow() {
|
|
382 |
onConnect={onConnect}
|
383 |
proOptions={{ hideAttribution: true }}
|
384 |
maxZoom={1}
|
385 |
-
minZoom={0.
|
|
|
|
|
386 |
defaultEdgeOptions={{
|
387 |
markerEnd: {
|
388 |
type: MarkerType.ArrowClosed,
|
|
|
1 |
+
// The LynxKite workspace editor.
|
2 |
+
|
3 |
import { getYjsDoc, syncedStore } from "@syncedstore/core";
|
4 |
import {
|
5 |
type Connection,
|
|
|
17 |
useUpdateNodeInternals,
|
18 |
} from "@xyflow/react";
|
19 |
import axios from "axios";
|
20 |
+
import { type MouseEvent, useCallback, useEffect, useMemo, useState } from "react";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
import { useParams } from "react-router";
|
22 |
import useSWR, { type Fetcher } from "swr";
|
23 |
import { WebsocketProvider } from "y-websocket";
|
|
|
32 |
// import NodeWithTableView from './NodeWithTableView';
|
33 |
import EnvironmentSelector from "./EnvironmentSelector";
|
34 |
import { LynxKiteState } from "./LynxKiteState";
|
35 |
+
import NodeSearch, { type OpsOp, type Catalog, type Catalogs } from "./NodeSearch.tsx";
|
|
|
|
|
|
|
|
|
36 |
import NodeWithGraphCreationView from "./nodes/GraphCreationNode.tsx";
|
37 |
import NodeWithImage from "./nodes/NodeWithImage.tsx";
|
38 |
import NodeWithParams from "./nodes/NodeWithParams";
|
|
|
53 |
const [nodes, setNodes] = useState([] as Node[]);
|
54 |
const [edges, setEdges] = useState([] as Edge[]);
|
55 |
const { path } = useParams();
|
56 |
+
const shortPath = path!
|
57 |
+
.split("/")
|
58 |
+
.pop()!
|
59 |
+
.replace(/[.]lynxkite[.]json$/, "");
|
60 |
const [state, setState] = useState({ workspace: {} as Workspace });
|
61 |
const [message, setMessage] = useState(null as string | null);
|
62 |
useEffect(() => {
|
|
|
64 |
setState(state);
|
65 |
const doc = getYjsDoc(state);
|
66 |
const proto = location.protocol === "https:" ? "wss:" : "ws:";
|
67 |
+
const wsProvider = new WebsocketProvider(`${proto}//${location.host}/ws/crdt`, path!, doc);
|
|
|
|
|
|
|
|
|
68 |
const onChange = (_update: any, origin: any, _doc: any, _tr: any) => {
|
69 |
if (origin === wsProvider) {
|
70 |
// An update from the CRDT. Apply it to the local state.
|
|
|
156 |
|
157 |
const fetcher: Fetcher<Catalogs> = (resource: string, init?: RequestInit) =>
|
158 |
fetch(resource, init).then((res) => res.json());
|
159 |
+
const catalog = useSWR(`/api/catalog?workspace=${path}`, fetcher);
|
160 |
const [suppressSearchUntil, setSuppressSearchUntil] = useState(0);
|
161 |
const [nodeSearchSettings, setNodeSearchSettings] = useState(
|
162 |
undefined as
|
|
|
181 |
useEffect(() => {
|
182 |
const handleKeyDown = (event: KeyboardEvent) => {
|
183 |
// Show the node search dialog on "/".
|
184 |
+
if (event.key === "/" && !nodeSearchSettings && !isTypingInFormElement()) {
|
|
|
|
|
|
|
|
|
185 |
event.preventDefault();
|
186 |
setNodeSearchSettings({
|
187 |
pos: { x: 100, y: 100 },
|
|
|
225 |
},
|
226 |
[catalog, state, nodeSearchSettings, suppressSearchUntil, closeNodeSearch],
|
227 |
);
|
228 |
+
function addNode(node: Partial<WorkspaceNode>, state: { workspace: Workspace }, nodes: Node[]) {
|
|
|
|
|
|
|
|
|
229 |
const title = node.data?.title;
|
230 |
let i = 1;
|
231 |
node.id = `${title} ${i}`;
|
|
|
243 |
data: {
|
244 |
meta: meta,
|
245 |
title: meta.name,
|
246 |
+
params: Object.fromEntries(Object.values(meta.params).map((p) => [p.name, p.default])),
|
|
|
|
|
247 |
},
|
248 |
};
|
249 |
return node;
|
|
|
291 |
try {
|
292 |
await axios.post("/api/upload", formData, {
|
293 |
onUploadProgress: (progressEvent) => {
|
294 |
+
const percentCompleted = Math.round((100 * progressEvent.loaded) / progressEvent.total!);
|
|
|
|
|
295 |
if (percentCompleted === 100) setMessage("Processing file...");
|
296 |
else setMessage(`Uploading ${percentCompleted}%`);
|
297 |
},
|
|
|
324 |
<a className="logo" href="">
|
325 |
<img alt="" src={favicon} />
|
326 |
</a>
|
327 |
+
<div className="ws-name">{shortPath}</div>
|
328 |
+
<title>{shortPath}</title>
|
329 |
<EnvironmentSelector
|
330 |
options={Object.keys(catalog.data || {})}
|
331 |
value={state.workspace.env!}
|
|
|
345 |
</a>
|
346 |
</div>
|
347 |
</div>
|
348 |
+
<div style={{ height: "100%", width: "100vw" }} onDragOver={onDragOver} onDrop={onDrop}>
|
|
|
|
|
|
|
|
|
349 |
<LynxKiteState.Provider value={state}>
|
350 |
<ReactFlow
|
351 |
nodes={nodes}
|
|
|
358 |
onConnect={onConnect}
|
359 |
proOptions={{ hideAttribution: true }}
|
360 |
maxZoom={1}
|
361 |
+
minZoom={0.2}
|
362 |
+
zoomOnScroll={false}
|
363 |
+
preventScrolling={false}
|
364 |
defaultEdgeOptions={{
|
365 |
markerEnd: {
|
366 |
type: MarkerType.ArrowClosed,
|
lynxkite-app/web/src/workspace/nodes/GraphCreationNode.tsx
CHANGED
@@ -20,12 +20,7 @@ function toMD(v: any): string {
|
|
20 |
function displayTable(name: string, df: any) {
|
21 |
if (df.data.length > 1) {
|
22 |
return (
|
23 |
-
<Table
|
24 |
-
key={`${name}-table`}
|
25 |
-
name={`${name}-table`}
|
26 |
-
columns={df.columns}
|
27 |
-
data={df.data}
|
28 |
-
/>
|
29 |
);
|
30 |
}
|
31 |
if (df.data.length) {
|
@@ -60,9 +55,7 @@ export default function NodeWithGraphCreationView(props: any) {
|
|
60 |
const display = props.data.display?.value;
|
61 |
const tables = display?.dataframes || {};
|
62 |
const singleTable = tables && Object.keys(tables).length === 1;
|
63 |
-
const [relations, setRelations] = useState(
|
64 |
-
relationsToDict(display?.relations) || {},
|
65 |
-
);
|
66 |
const singleRelation = relations && Object.keys(relations).length === 1;
|
67 |
function setParam(name: string, newValue: any, opts: UpdateOptions) {
|
68 |
reactFlow.updateNodeData(props.id, {
|
@@ -211,9 +204,7 @@ export default function NodeWithGraphCreationView(props: any) {
|
|
211 |
|
212 |
<datalist id="edges-column-options">
|
213 |
{tables[relation.source_table] &&
|
214 |
-
tables[relation.df].columns.map((name: string) =>
|
215 |
-
<option key={name} value={name} />
|
216 |
-
))}
|
217 |
</datalist>
|
218 |
|
219 |
<datalist id="source-node-column-options">
|
@@ -274,10 +265,7 @@ export default function NodeWithGraphCreationView(props: any) {
|
|
274 |
<div className="graph-relations">
|
275 |
<div className="graph-table-header">
|
276 |
Relationships
|
277 |
-
<button
|
278 |
-
className="add-relationship-button"
|
279 |
-
onClick={(_) => addRelation()}
|
280 |
-
>
|
281 |
+
|
282 |
</button>
|
283 |
</div>
|
|
|
20 |
function displayTable(name: string, df: any) {
|
21 |
if (df.data.length > 1) {
|
22 |
return (
|
23 |
+
<Table key={`${name}-table`} name={`${name}-table`} columns={df.columns} data={df.data} />
|
|
|
|
|
|
|
|
|
|
|
24 |
);
|
25 |
}
|
26 |
if (df.data.length) {
|
|
|
55 |
const display = props.data.display?.value;
|
56 |
const tables = display?.dataframes || {};
|
57 |
const singleTable = tables && Object.keys(tables).length === 1;
|
58 |
+
const [relations, setRelations] = useState(relationsToDict(display?.relations) || {});
|
|
|
|
|
59 |
const singleRelation = relations && Object.keys(relations).length === 1;
|
60 |
function setParam(name: string, newValue: any, opts: UpdateOptions) {
|
61 |
reactFlow.updateNodeData(props.id, {
|
|
|
204 |
|
205 |
<datalist id="edges-column-options">
|
206 |
{tables[relation.source_table] &&
|
207 |
+
tables[relation.df].columns.map((name: string) => <option key={name} value={name} />)}
|
|
|
|
|
208 |
</datalist>
|
209 |
|
210 |
<datalist id="source-node-column-options">
|
|
|
265 |
<div className="graph-relations">
|
266 |
<div className="graph-table-header">
|
267 |
Relationships
|
268 |
+
<button className="add-relationship-button" onClick={(_) => addRelation()}>
|
|
|
|
|
|
|
269 |
+
|
270 |
</button>
|
271 |
</div>
|
lynxkite-app/web/src/workspace/nodes/LynxKiteNode.tsx
CHANGED
@@ -1,9 +1,4 @@
|
|
1 |
-
import {
|
2 |
-
Handle,
|
3 |
-
NodeResizeControl,
|
4 |
-
type Position,
|
5 |
-
useReactFlow,
|
6 |
-
} from "@xyflow/react";
|
7 |
// @ts-ignore
|
8 |
import ChevronDownRight from "~icons/tabler/chevron-down-right.jsx";
|
9 |
|
@@ -38,10 +33,8 @@ function getHandles(inputs: object, outputs: object) {
|
|
38 |
}
|
39 |
for (const e of handles) {
|
40 |
e.offsetPercentage = (100 * (e.index + 1)) / (counts[e.position] + 1);
|
41 |
-
const simpleHorizontal =
|
42 |
-
|
43 |
-
const simpleVertical =
|
44 |
-
counts.left === 0 && counts.right === 0 && handles.length <= 2;
|
45 |
e.showLabel = !simpleHorizontal && !simpleVertical;
|
46 |
}
|
47 |
return handles;
|
@@ -71,10 +64,7 @@ export default function LynxKiteNode(props: LynxKiteNodeProps) {
|
|
71 |
}}
|
72 |
>
|
73 |
<div className="lynxkite-node" style={props.nodeStyle}>
|
74 |
-
<div
|
75 |
-
className={`title bg-primary ${data.status}`}
|
76 |
-
onClick={titleClicked}
|
77 |
-
>
|
78 |
{data.title}
|
79 |
{data.error && <span className="title-icon">⚠️</span>}
|
80 |
{expanded || <span className="title-icon">⋯</span>}
|
@@ -99,14 +89,11 @@ export default function LynxKiteNode(props: LynxKiteNodeProps) {
|
|
99 |
type={handle.type}
|
100 |
position={handle.position as Position}
|
101 |
style={{
|
102 |
-
[handleOffsetDirection[handle.position]]:
|
103 |
-
`${handle.offsetPercentage}% `,
|
104 |
}}
|
105 |
>
|
106 |
{handle.showLabel && (
|
107 |
-
<span className="handle-name">
|
108 |
-
{handle.name.replace(/_/g, " ")}
|
109 |
-
</span>
|
110 |
)}
|
111 |
</Handle>
|
112 |
))}
|
|
|
1 |
+
import { Handle, NodeResizeControl, type Position, useReactFlow } from "@xyflow/react";
|
|
|
|
|
|
|
|
|
|
|
2 |
// @ts-ignore
|
3 |
import ChevronDownRight from "~icons/tabler/chevron-down-right.jsx";
|
4 |
|
|
|
33 |
}
|
34 |
for (const e of handles) {
|
35 |
e.offsetPercentage = (100 * (e.index + 1)) / (counts[e.position] + 1);
|
36 |
+
const simpleHorizontal = counts.top === 0 && counts.bottom === 0 && handles.length <= 2;
|
37 |
+
const simpleVertical = counts.left === 0 && counts.right === 0 && handles.length <= 2;
|
|
|
|
|
38 |
e.showLabel = !simpleHorizontal && !simpleVertical;
|
39 |
}
|
40 |
return handles;
|
|
|
64 |
}}
|
65 |
>
|
66 |
<div className="lynxkite-node" style={props.nodeStyle}>
|
67 |
+
<div className={`title bg-primary ${data.status}`} onClick={titleClicked}>
|
|
|
|
|
|
|
68 |
{data.title}
|
69 |
{data.error && <span className="title-icon">⚠️</span>}
|
70 |
{expanded || <span className="title-icon">⋯</span>}
|
|
|
89 |
type={handle.type}
|
90 |
position={handle.position as Position}
|
91 |
style={{
|
92 |
+
[handleOffsetDirection[handle.position]]: `${handle.offsetPercentage}% `,
|
|
|
93 |
}}
|
94 |
>
|
95 |
{handle.showLabel && (
|
96 |
+
<span className="handle-name">{handle.name.replace(/_/g, " ")}</span>
|
|
|
|
|
97 |
)}
|
98 |
</Handle>
|
99 |
))}
|
lynxkite-app/web/src/workspace/nodes/NodeGroupParameter.tsx
CHANGED
@@ -24,6 +24,7 @@ interface GroupsType {
|
|
24 |
interface NodeGroupParameterProps {
|
25 |
meta: { selector: SelectorType; groups: GroupsType };
|
26 |
value: any;
|
|
|
27 |
setParam: (name: string, value: any, options?: { delay: number }) => void;
|
28 |
deleteParam: (name: string, options?: { delay: number }) => void;
|
29 |
}
|
@@ -31,14 +32,13 @@ interface NodeGroupParameterProps {
|
|
31 |
export default function NodeGroupParameter({
|
32 |
meta,
|
33 |
value,
|
|
|
34 |
setParam,
|
35 |
deleteParam,
|
36 |
}: NodeGroupParameterProps) {
|
37 |
const selector = meta.selector;
|
38 |
const groups = meta.groups;
|
39 |
-
const [selectedValue, setSelectedValue] = useState<string>(
|
40 |
-
value || selector.default,
|
41 |
-
);
|
42 |
|
43 |
const handleSelectorChange = (value: any, opts?: { delay: number }) => {
|
44 |
setSelectedValue(value);
|
@@ -47,9 +47,7 @@ export default function NodeGroupParameter({
|
|
47 |
|
48 |
useEffect(() => {
|
49 |
// Clean possible previous parameters first
|
50 |
-
Object.values(groups).flatMap((group) =>
|
51 |
-
group.map((entry) => deleteParam(entry.name)),
|
52 |
-
);
|
53 |
for (const param of groups[selectedValue]) {
|
54 |
setParam(param.name, param.default);
|
55 |
}
|
@@ -60,6 +58,7 @@ export default function NodeGroupParameter({
|
|
60 |
name={selector.name}
|
61 |
key={selector.name}
|
62 |
value={selectedValue}
|
|
|
63 |
meta={selector}
|
64 |
onChange={handleSelectorChange}
|
65 |
/>
|
|
|
24 |
interface NodeGroupParameterProps {
|
25 |
meta: { selector: SelectorType; groups: GroupsType };
|
26 |
value: any;
|
27 |
+
data: any;
|
28 |
setParam: (name: string, value: any, options?: { delay: number }) => void;
|
29 |
deleteParam: (name: string, options?: { delay: number }) => void;
|
30 |
}
|
|
|
32 |
export default function NodeGroupParameter({
|
33 |
meta,
|
34 |
value,
|
35 |
+
data,
|
36 |
setParam,
|
37 |
deleteParam,
|
38 |
}: NodeGroupParameterProps) {
|
39 |
const selector = meta.selector;
|
40 |
const groups = meta.groups;
|
41 |
+
const [selectedValue, setSelectedValue] = useState<string>(value || selector.default);
|
|
|
|
|
42 |
|
43 |
const handleSelectorChange = (value: any, opts?: { delay: number }) => {
|
44 |
setSelectedValue(value);
|
|
|
47 |
|
48 |
useEffect(() => {
|
49 |
// Clean possible previous parameters first
|
50 |
+
Object.values(groups).flatMap((group) => group.map((entry) => deleteParam(entry.name)));
|
|
|
|
|
51 |
for (const param of groups[selectedValue]) {
|
52 |
setParam(param.name, param.default);
|
53 |
}
|
|
|
58 |
name={selector.name}
|
59 |
key={selector.name}
|
60 |
value={selectedValue}
|
61 |
+
data={data}
|
62 |
meta={selector}
|
63 |
onChange={handleSelectorChange}
|
64 |
/>
|
lynxkite-app/web/src/workspace/nodes/NodeParameter.tsx
CHANGED
@@ -1,8 +1,186 @@
|
|
1 |
-
|
|
|
|
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
function ParamName({ name }: { name: string }) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
return (
|
5 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
);
|
7 |
}
|
8 |
|
@@ -10,15 +188,11 @@ interface NodeParameterProps {
|
|
10 |
name: string;
|
11 |
value: any;
|
12 |
meta: any;
|
|
|
13 |
onChange: (value: any, options?: { delay: number }) => void;
|
14 |
}
|
15 |
|
16 |
-
export default function NodeParameter({
|
17 |
-
name,
|
18 |
-
value,
|
19 |
-
meta,
|
20 |
-
onChange,
|
21 |
-
}: NodeParameterProps) {
|
22 |
return (
|
23 |
// biome-ignore lint/a11y/noLabelWithoutControl: Most of the time there is a control.
|
24 |
<label className="param">
|
@@ -56,28 +230,34 @@ export default function NodeParameter({
|
|
56 |
) : meta?.type?.type === BOOLEAN ? (
|
57 |
<div className="form-control">
|
58 |
<label className="label cursor-pointer">
|
|
|
59 |
<input
|
60 |
className="checkbox"
|
61 |
type="checkbox"
|
62 |
checked={value}
|
63 |
onChange={(evt) => onChange(evt.currentTarget.checked)}
|
64 |
/>
|
65 |
-
{name.replace(/_/g, " ")}
|
66 |
</label>
|
67 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
) : (
|
69 |
<>
|
70 |
<ParamName name={name} />
|
71 |
-
<
|
72 |
-
className="input input-bordered w-full"
|
73 |
-
value={value || ""}
|
74 |
-
onChange={(evt) => onChange(evt.currentTarget.value, { delay: 2 })}
|
75 |
-
onBlur={(evt) => onChange(evt.currentTarget.value, { delay: 0 })}
|
76 |
-
onKeyDown={(evt) =>
|
77 |
-
evt.code === "Enter" &&
|
78 |
-
onChange(evt.currentTarget.value, { delay: 0 })
|
79 |
-
}
|
80 |
-
/>
|
81 |
</>
|
82 |
)}
|
83 |
</label>
|
|
|
1 |
+
import { useRef } from "react";
|
2 |
+
// @ts-ignore
|
3 |
+
import ArrowsHorizontal from "~icons/tabler/arrows-horizontal.jsx";
|
4 |
|
5 |
+
const BOOLEAN = "<class 'bool'>";
|
6 |
+
const MODEL_TRAINING_INPUT_MAPPING =
|
7 |
+
"<class 'lynxkite_graph_analytics.ml_ops.ModelTrainingInputMapping'>";
|
8 |
+
const MODEL_INFERENCE_INPUT_MAPPING =
|
9 |
+
"<class 'lynxkite_graph_analytics.ml_ops.ModelInferenceInputMapping'>";
|
10 |
+
const MODEL_OUTPUT_MAPPING = "<class 'lynxkite_graph_analytics.ml_ops.ModelOutputMapping'>";
|
11 |
function ParamName({ name }: { name: string }) {
|
12 |
+
return <span className="param-name bg-base-200">{name.replace(/_/g, " ")}</span>;
|
13 |
+
}
|
14 |
+
|
15 |
+
function Input({
|
16 |
+
value,
|
17 |
+
onChange,
|
18 |
+
inputRef,
|
19 |
+
}: {
|
20 |
+
value: string;
|
21 |
+
onChange: (value: string, options?: { delay: number }) => void;
|
22 |
+
inputRef?: React.Ref<HTMLInputElement>;
|
23 |
+
}) {
|
24 |
+
return (
|
25 |
+
<input
|
26 |
+
className="input input-bordered w-full"
|
27 |
+
ref={inputRef}
|
28 |
+
value={value ?? ""}
|
29 |
+
onChange={(evt) => onChange(evt.currentTarget.value, { delay: 2 })}
|
30 |
+
onBlur={(evt) => onChange(evt.currentTarget.value, { delay: 0 })}
|
31 |
+
onKeyDown={(evt) => evt.code === "Enter" && onChange(evt.currentTarget.value, { delay: 0 })}
|
32 |
+
/>
|
33 |
+
);
|
34 |
+
}
|
35 |
+
|
36 |
+
type Bindings = {
|
37 |
+
[key: string]: {
|
38 |
+
df: string;
|
39 |
+
column: string;
|
40 |
+
};
|
41 |
+
};
|
42 |
+
|
43 |
+
function getModelBindings(
|
44 |
+
data: any,
|
45 |
+
variant: "training input" | "inference input" | "output",
|
46 |
+
): string[] {
|
47 |
+
function bindingsOfModel(m: any): string[] {
|
48 |
+
switch (variant) {
|
49 |
+
case "training input":
|
50 |
+
return [...m.inputs, ...m.loss_inputs.filter((i: string) => !m.outputs.includes(i))];
|
51 |
+
case "inference input":
|
52 |
+
return m.inputs;
|
53 |
+
case "output":
|
54 |
+
return m.outputs;
|
55 |
+
}
|
56 |
+
}
|
57 |
+
const bindings = new Set<string>();
|
58 |
+
const inputs = data?.input_metadata?.value ?? data?.input_metadata ?? [];
|
59 |
+
for (const input of inputs) {
|
60 |
+
const other = input.other ?? {};
|
61 |
+
for (const e of Object.values(other) as any[]) {
|
62 |
+
if (e.type === "model") {
|
63 |
+
for (const b of bindingsOfModel(e.model)) {
|
64 |
+
bindings.add(b);
|
65 |
+
}
|
66 |
+
}
|
67 |
+
}
|
68 |
+
}
|
69 |
+
const list = [...bindings];
|
70 |
+
list.sort();
|
71 |
+
return list;
|
72 |
+
}
|
73 |
+
|
74 |
+
function parseJsonOrEmpty(json: string): object {
|
75 |
+
try {
|
76 |
+
const j = JSON.parse(json);
|
77 |
+
if (j !== null && typeof j === "object") {
|
78 |
+
return j;
|
79 |
+
}
|
80 |
+
} catch (e) {}
|
81 |
+
return {};
|
82 |
+
}
|
83 |
+
|
84 |
+
function ModelMapping({ value, onChange, data, variant }: any) {
|
85 |
+
const dfsRef = useRef({} as { [binding: string]: HTMLSelectElement | null });
|
86 |
+
const columnsRef = useRef(
|
87 |
+
{} as { [binding: string]: HTMLSelectElement | HTMLInputElement | null },
|
88 |
+
);
|
89 |
+
const v: any = parseJsonOrEmpty(value);
|
90 |
+
v.map ??= {};
|
91 |
+
const dfs: { [df: string]: string[] } = {};
|
92 |
+
const inputs = data?.input_metadata?.value ?? data?.input_metadata ?? [];
|
93 |
+
for (const input of inputs) {
|
94 |
+
if (!input.dataframes) continue;
|
95 |
+
const dataframes = input.dataframes as {
|
96 |
+
[df: string]: { columns: string[] };
|
97 |
+
};
|
98 |
+
for (const [df, { columns }] of Object.entries(dataframes)) {
|
99 |
+
dfs[df] = columns;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
const bindings = getModelBindings(data, variant);
|
103 |
+
function getMap() {
|
104 |
+
const map: Bindings = {};
|
105 |
+
for (const binding of bindings) {
|
106 |
+
const df = dfsRef.current[binding]?.value ?? "";
|
107 |
+
const column = columnsRef.current[binding]?.value ?? "";
|
108 |
+
if (df.length || column.length) {
|
109 |
+
map[binding] = { df, column };
|
110 |
+
}
|
111 |
+
}
|
112 |
+
return map;
|
113 |
+
}
|
114 |
return (
|
115 |
+
<table className="model-mapping-param">
|
116 |
+
<tbody>
|
117 |
+
{bindings.length > 0 ? (
|
118 |
+
bindings.map((binding: string) => (
|
119 |
+
<tr key={binding}>
|
120 |
+
<td>{binding}</td>
|
121 |
+
<td>
|
122 |
+
<ArrowsHorizontal />
|
123 |
+
</td>
|
124 |
+
<td>
|
125 |
+
<select
|
126 |
+
className="select select-ghost"
|
127 |
+
value={v.map?.[binding]?.df}
|
128 |
+
ref={(el) => {
|
129 |
+
dfsRef.current[binding] = el;
|
130 |
+
}}
|
131 |
+
onChange={() => onChange(JSON.stringify({ map: getMap() }))}
|
132 |
+
>
|
133 |
+
<option key="" value="" />
|
134 |
+
{Object.keys(dfs).map((df: string) => (
|
135 |
+
<option key={df} value={df}>
|
136 |
+
{df}
|
137 |
+
</option>
|
138 |
+
))}
|
139 |
+
</select>
|
140 |
+
</td>
|
141 |
+
<td>
|
142 |
+
{variant === "output" ? (
|
143 |
+
<Input
|
144 |
+
inputRef={(el) => {
|
145 |
+
columnsRef.current[binding] = el;
|
146 |
+
}}
|
147 |
+
value={v.map?.[binding]?.column}
|
148 |
+
onChange={(column, options) => {
|
149 |
+
const map = getMap();
|
150 |
+
// At this point the <input> has not been updated yet. We use the value from the event.
|
151 |
+
const df = dfsRef.current[binding]?.value ?? "";
|
152 |
+
map[binding] ??= { df, column };
|
153 |
+
map[binding].column = column;
|
154 |
+
onChange(JSON.stringify({ map }), options);
|
155 |
+
}}
|
156 |
+
/>
|
157 |
+
) : (
|
158 |
+
<select
|
159 |
+
className="select select-ghost"
|
160 |
+
value={v.map?.[binding]?.column}
|
161 |
+
ref={(el) => {
|
162 |
+
columnsRef.current[binding] = el;
|
163 |
+
}}
|
164 |
+
onChange={() => onChange(JSON.stringify({ map: getMap() }))}
|
165 |
+
>
|
166 |
+
<option key="" value="" />
|
167 |
+
{dfs[v.map?.[binding]?.df]?.map((col: string) => (
|
168 |
+
<option key={col} value={col}>
|
169 |
+
{col}
|
170 |
+
</option>
|
171 |
+
))}
|
172 |
+
</select>
|
173 |
+
)}
|
174 |
+
</td>
|
175 |
+
</tr>
|
176 |
+
))
|
177 |
+
) : (
|
178 |
+
<tr>
|
179 |
+
<td>no bindings</td>
|
180 |
+
</tr>
|
181 |
+
)}
|
182 |
+
</tbody>
|
183 |
+
</table>
|
184 |
);
|
185 |
}
|
186 |
|
|
|
188 |
name: string;
|
189 |
value: any;
|
190 |
meta: any;
|
191 |
+
data: any;
|
192 |
onChange: (value: any, options?: { delay: number }) => void;
|
193 |
}
|
194 |
|
195 |
+
export default function NodeParameter({ name, value, meta, data, onChange }: NodeParameterProps) {
|
|
|
|
|
|
|
|
|
|
|
196 |
return (
|
197 |
// biome-ignore lint/a11y/noLabelWithoutControl: Most of the time there is a control.
|
198 |
<label className="param">
|
|
|
230 |
) : meta?.type?.type === BOOLEAN ? (
|
231 |
<div className="form-control">
|
232 |
<label className="label cursor-pointer">
|
233 |
+
{name.replace(/_/g, " ")}
|
234 |
<input
|
235 |
className="checkbox"
|
236 |
type="checkbox"
|
237 |
checked={value}
|
238 |
onChange={(evt) => onChange(evt.currentTarget.checked)}
|
239 |
/>
|
|
|
240 |
</label>
|
241 |
</div>
|
242 |
+
) : meta?.type?.type === MODEL_TRAINING_INPUT_MAPPING ? (
|
243 |
+
<>
|
244 |
+
<ParamName name={name} />
|
245 |
+
<ModelMapping value={value} data={data} variant="training input" onChange={onChange} />
|
246 |
+
</>
|
247 |
+
) : meta?.type?.type === MODEL_INFERENCE_INPUT_MAPPING ? (
|
248 |
+
<>
|
249 |
+
<ParamName name={name} />
|
250 |
+
<ModelMapping value={value} data={data} variant="inference input" onChange={onChange} />
|
251 |
+
</>
|
252 |
+
) : meta?.type?.type === MODEL_OUTPUT_MAPPING ? (
|
253 |
+
<>
|
254 |
+
<ParamName name={name} />
|
255 |
+
<ModelMapping value={value} data={data} variant="output" onChange={onChange} />
|
256 |
+
</>
|
257 |
) : (
|
258 |
<>
|
259 |
<ParamName name={name} />
|
260 |
+
<Input value={value} onChange={onChange} />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
261 |
</>
|
262 |
)}
|
263 |
</label>
|
lynxkite-app/web/src/workspace/nodes/NodeWithImage.tsx
CHANGED
@@ -3,9 +3,7 @@ import NodeWithParams from "./NodeWithParams";
|
|
3 |
const NodeWithImage = (props: any) => {
|
4 |
return (
|
5 |
<NodeWithParams {...props}>
|
6 |
-
{props.data.display &&
|
7 |
-
<img src={props.data.display} alt="Node Display" />
|
8 |
-
)}
|
9 |
</NodeWithParams>
|
10 |
);
|
11 |
};
|
|
|
3 |
const NodeWithImage = (props: any) => {
|
4 |
return (
|
5 |
<NodeWithParams {...props}>
|
6 |
+
{props.data.display && <img src={props.data.display} alt="Node Display" />}
|
|
|
|
|
7 |
</NodeWithParams>
|
8 |
);
|
9 |
};
|
lynxkite-app/web/src/workspace/nodes/NodeWithParams.tsx
CHANGED
@@ -35,11 +35,8 @@ function NodeWithParams(props: any) {
|
|
35 |
|
36 |
return (
|
37 |
<LynxKiteNode {...props}>
|
38 |
-
{props.collapsed && (
|
39 |
-
<div
|
40 |
-
className="params-expander"
|
41 |
-
onClick={() => setCollapsed(!collapsed)}
|
42 |
-
>
|
43 |
<Triangle className={`flippy ${collapsed ? "flippy-90" : ""}`} />
|
44 |
</div>
|
45 |
)}
|
@@ -49,23 +46,21 @@ function NodeWithParams(props: any) {
|
|
49 |
<NodeGroupParameter
|
50 |
key={name}
|
51 |
value={value}
|
|
|
52 |
meta={metaParams?.[name]}
|
53 |
setParam={(name: string, value: any, opts?: UpdateOptions) =>
|
54 |
setParam(name, value, opts || {})
|
55 |
}
|
56 |
-
deleteParam={(name: string, opts?: UpdateOptions) =>
|
57 |
-
deleteParam(name, opts || {})
|
58 |
-
}
|
59 |
/>
|
60 |
) : (
|
61 |
<NodeParameter
|
62 |
name={name}
|
63 |
key={name}
|
64 |
value={value}
|
|
|
65 |
meta={metaParams?.[name]}
|
66 |
-
onChange={(value: any, opts?: UpdateOptions) =>
|
67 |
-
setParam(name, value, opts || {})
|
68 |
-
}
|
69 |
/>
|
70 |
),
|
71 |
)}
|
|
|
35 |
|
36 |
return (
|
37 |
<LynxKiteNode {...props}>
|
38 |
+
{props.collapsed && params.length > 0 && (
|
39 |
+
<div className="params-expander" onClick={() => setCollapsed(!collapsed)}>
|
|
|
|
|
|
|
40 |
<Triangle className={`flippy ${collapsed ? "flippy-90" : ""}`} />
|
41 |
</div>
|
42 |
)}
|
|
|
46 |
<NodeGroupParameter
|
47 |
key={name}
|
48 |
value={value}
|
49 |
+
data={props.data}
|
50 |
meta={metaParams?.[name]}
|
51 |
setParam={(name: string, value: any, opts?: UpdateOptions) =>
|
52 |
setParam(name, value, opts || {})
|
53 |
}
|
54 |
+
deleteParam={(name: string, opts?: UpdateOptions) => deleteParam(name, opts || {})}
|
|
|
|
|
55 |
/>
|
56 |
) : (
|
57 |
<NodeParameter
|
58 |
name={name}
|
59 |
key={name}
|
60 |
value={value}
|
61 |
+
data={props.data}
|
62 |
meta={metaParams?.[name]}
|
63 |
+
onChange={(value: any, opts?: UpdateOptions) => setParam(name, value, opts || {})}
|
|
|
|
|
64 |
/>
|
65 |
),
|
66 |
)}
|
lynxkite-app/web/src/workspace/nodes/NodeWithTableView.tsx
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import { useState } from "react";
|
2 |
import React from "react";
|
3 |
import Markdown from "react-markdown";
|
@@ -14,34 +15,41 @@ function toMD(v: any): string {
|
|
14 |
return JSON.stringify(v);
|
15 |
}
|
16 |
|
|
|
|
|
17 |
export default function NodeWithTableView(props: any) {
|
18 |
-
const
|
|
|
19 |
const display = props.data.display?.value;
|
20 |
-
const single =
|
21 |
-
display?.dataframes && Object.keys(display?.dataframes).length === 1;
|
22 |
const dfs = Object.entries(display?.dataframes || {});
|
23 |
dfs.sort();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
return (
|
25 |
<LynxKiteNode {...props}>
|
26 |
{display && [
|
27 |
dfs.map(([name, df]: [string, any]) => (
|
28 |
<React.Fragment key={name}>
|
29 |
{!single && (
|
30 |
-
<div
|
31 |
-
key={`${name}-header`}
|
32 |
-
className="df-head"
|
33 |
-
onClick={() => setOpen({ ...open, [name]: !open[name] })}
|
34 |
-
>
|
35 |
{name}
|
36 |
</div>
|
37 |
)}
|
38 |
{(single || open[name]) &&
|
39 |
(df.data.length > 1 ? (
|
40 |
-
<Table
|
41 |
-
key={`${name}-table`}
|
42 |
-
columns={df.columns}
|
43 |
-
data={df.data}
|
44 |
-
/>
|
45 |
) : df.data.length ? (
|
46 |
<dl key={`${name}-dl`}>
|
47 |
{df.columns.map((c: string, i: number) => (
|
@@ -60,11 +68,7 @@ export default function NodeWithTableView(props: any) {
|
|
60 |
)),
|
61 |
Object.entries(display.others || {}).map(([name, o]) => (
|
62 |
<>
|
63 |
-
<div
|
64 |
-
key={`${name}-header`}
|
65 |
-
className="df-head"
|
66 |
-
onClick={() => setOpen({ ...open, [name]: !open[name] })}
|
67 |
-
>
|
68 |
{name}
|
69 |
</div>
|
70 |
{open[name] && <pre>{(o as any).toString()}</pre>}
|
|
|
1 |
+
import { useReactFlow } from "@xyflow/react";
|
2 |
import { useState } from "react";
|
3 |
import React from "react";
|
4 |
import Markdown from "react-markdown";
|
|
|
15 |
return JSON.stringify(v);
|
16 |
}
|
17 |
|
18 |
+
type OpenState = { [name: string]: boolean };
|
19 |
+
|
20 |
export default function NodeWithTableView(props: any) {
|
21 |
+
const reactFlow = useReactFlow();
|
22 |
+
const [open, setOpen] = useState((props.data?.params?._tables_open ?? {}) as OpenState);
|
23 |
const display = props.data.display?.value;
|
24 |
+
const single = display?.dataframes && Object.keys(display?.dataframes).length === 1;
|
|
|
25 |
const dfs = Object.entries(display?.dataframes || {});
|
26 |
dfs.sort();
|
27 |
+
function setParam(name: string, newValue: any) {
|
28 |
+
reactFlow.updateNodeData(props.id, (prevData: any) => ({
|
29 |
+
...prevData,
|
30 |
+
params: { ...prevData.data.params, [name]: newValue },
|
31 |
+
}));
|
32 |
+
}
|
33 |
+
function toggleTable(name: string) {
|
34 |
+
setOpen((prevOpen: OpenState) => {
|
35 |
+
const newOpen = { ...prevOpen, [name]: !prevOpen[name] };
|
36 |
+
setParam("_tables_open", newOpen);
|
37 |
+
return newOpen;
|
38 |
+
});
|
39 |
+
}
|
40 |
return (
|
41 |
<LynxKiteNode {...props}>
|
42 |
{display && [
|
43 |
dfs.map(([name, df]: [string, any]) => (
|
44 |
<React.Fragment key={name}>
|
45 |
{!single && (
|
46 |
+
<div key={`${name}-header`} className="df-head" onClick={() => toggleTable(name)}>
|
|
|
|
|
|
|
|
|
47 |
{name}
|
48 |
</div>
|
49 |
)}
|
50 |
{(single || open[name]) &&
|
51 |
(df.data.length > 1 ? (
|
52 |
+
<Table key={`${name}-table`} columns={df.columns} data={df.data} />
|
|
|
|
|
|
|
|
|
53 |
) : df.data.length ? (
|
54 |
<dl key={`${name}-dl`}>
|
55 |
{df.columns.map((c: string, i: number) => (
|
|
|
68 |
)),
|
69 |
Object.entries(display.others || {}).map(([name, o]) => (
|
70 |
<>
|
71 |
+
<div key={`${name}-header`} className="df-head" onClick={() => toggleTable(name)}>
|
|
|
|
|
|
|
|
|
72 |
{name}
|
73 |
</div>
|
74 |
{open[name] && <pre>{(o as any).toString()}</pre>}
|
lynxkite-app/web/src/workspace/nodes/NodeWithVisualization.tsx
CHANGED
@@ -8,6 +8,10 @@ const NodeWithVisualization = (props: any) => {
|
|
8 |
useEffect(() => {
|
9 |
const opts = props.data?.display?.value;
|
10 |
if (!opts || !chartsRef.current) return;
|
|
|
|
|
|
|
|
|
11 |
chartsInstanceRef.current = echarts.init(chartsRef.current, null, {
|
12 |
renderer: "canvas",
|
13 |
width: "auto",
|
|
|
8 |
useEffect(() => {
|
9 |
const opts = props.data?.display?.value;
|
10 |
if (!opts || !chartsRef.current) return;
|
11 |
+
if (opts.tooltip?.formatter === "GET_THIRD_VALUE") {
|
12 |
+
// We can't pass a function from the backend, and can't get good tooltips otherwise.
|
13 |
+
opts.tooltip.formatter = (params: any) => params.value[2];
|
14 |
+
}
|
15 |
chartsInstanceRef.current = echarts.init(chartsRef.current, null, {
|
16 |
renderer: "canvas",
|
17 |
width: "auto",
|
lynxkite-app/web/tests/directory.spec.ts
CHANGED
@@ -14,13 +14,6 @@ test.describe("Directory operations", () => {
|
|
14 |
splash = await Splash.open(page);
|
15 |
});
|
16 |
|
17 |
-
test("Create workspace with default name", async () => {
|
18 |
-
const workspace = await Workspace.empty(splash.page);
|
19 |
-
// Not checking for exact match, since there may be pre-existing "Untitled" workspaces
|
20 |
-
expect(workspace.name).toContain("Untitled");
|
21 |
-
await workspace.close();
|
22 |
-
});
|
23 |
-
|
24 |
test("Create & delete workspace", async () => {
|
25 |
const workspaceName = `TestWorkspace-${Date.now()}`;
|
26 |
const workspace = await Workspace.empty(splash.page, workspaceName);
|
@@ -40,11 +33,6 @@ test.describe("Directory operations", () => {
|
|
40 |
await splash.deleteEntry(folderName);
|
41 |
await expect(splash.getEntry(folderName)).not.toBeVisible();
|
42 |
});
|
43 |
-
|
44 |
-
test("Create folder with default name", async () => {
|
45 |
-
await splash.createFolder();
|
46 |
-
await expect(splash.currentFolder()).toContainText("Untitled");
|
47 |
-
});
|
48 |
});
|
49 |
|
50 |
test.describe
|
|
|
14 |
splash = await Splash.open(page);
|
15 |
});
|
16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
test("Create & delete workspace", async () => {
|
18 |
const workspaceName = `TestWorkspace-${Date.now()}`;
|
19 |
const workspace = await Workspace.empty(splash.page, workspaceName);
|
|
|
33 |
await splash.deleteEntry(folderName);
|
34 |
await expect(splash.getEntry(folderName)).not.toBeVisible();
|
35 |
});
|
|
|
|
|
|
|
|
|
|
|
36 |
});
|
37 |
|
38 |
test.describe
|
lynxkite-app/web/tests/errors.spec.ts
CHANGED
@@ -35,9 +35,7 @@ test("unknown operation", async () => {
|
|
35 |
await graphBox.getByLabel("n", { exact: true }).fill("10");
|
36 |
await workspace.setEnv("LynxScribe");
|
37 |
const csvBox = workspace.getBox("NX › Scale-Free Graph 1");
|
38 |
-
await expect(csvBox.locator(".error")).toHaveText(
|
39 |
-
'Operation "NX › Scale-Free Graph" not found.',
|
40 |
-
);
|
41 |
await workspace.setEnv("LynxKite Graph Analytics");
|
42 |
await expect(csvBox.locator(".error")).not.toBeVisible();
|
43 |
});
|
|
|
35 |
await graphBox.getByLabel("n", { exact: true }).fill("10");
|
36 |
await workspace.setEnv("LynxScribe");
|
37 |
const csvBox = workspace.getBox("NX › Scale-Free Graph 1");
|
38 |
+
await expect(csvBox.locator(".error")).toHaveText('Operation "NX › Scale-Free Graph" not found.');
|
|
|
|
|
39 |
await workspace.setEnv("LynxKite Graph Analytics");
|
40 |
await expect(csvBox.locator(".error")).not.toBeVisible();
|
41 |
});
|
lynxkite-app/web/tests/graph_creation.spec.ts
CHANGED
@@ -5,15 +5,9 @@ import { Splash, Workspace } from "./lynxkite";
|
|
5 |
let workspace: Workspace;
|
6 |
|
7 |
test.beforeEach(async ({ browser }) => {
|
8 |
-
workspace = await Workspace.empty(
|
9 |
-
await browser.newPage(),
|
10 |
-
"graph_creation_spec_test",
|
11 |
-
);
|
12 |
await workspace.addBox("NX › Scale-Free Graph");
|
13 |
-
await workspace
|
14 |
-
.getBox("NX › Scale-Free Graph 1")
|
15 |
-
.getByLabel("n", { exact: true })
|
16 |
-
.fill("10");
|
17 |
await workspace.addBox("Create graph");
|
18 |
await workspace.connectBoxes("NX › Scale-Free Graph 1", "Create graph 1");
|
19 |
});
|
@@ -45,9 +39,7 @@ test("Tables are displayed in the Graph creation box", async () => {
|
|
45 |
|
46 |
test("Adding and removing relationships", async () => {
|
47 |
const graphBox = await workspace.getBox("Create graph 1");
|
48 |
-
const addRelationshipButton = await graphBox.locator(
|
49 |
-
".add-relationship-button",
|
50 |
-
);
|
51 |
await addRelationshipButton.click();
|
52 |
const formData: Record<string, string> = {
|
53 |
name: "relation_1",
|
@@ -69,10 +61,9 @@ test("Adding and removing relationships", async () => {
|
|
69 |
// check that the relationship has been saved in the backend
|
70 |
await workspace.page.reload();
|
71 |
const graphBoxAfterReload = await workspace.getBox("Create graph 1");
|
72 |
-
const relationHeader = await graphBoxAfterReload.locator(
|
73 |
-
|
74 |
-
|
75 |
-
);
|
76 |
await expect(relationHeader).toBeVisible();
|
77 |
await relationHeader.locator("button").click(); // Delete the relationship
|
78 |
await expect(relationHeader).not.toBeVisible();
|
|
|
5 |
let workspace: Workspace;
|
6 |
|
7 |
test.beforeEach(async ({ browser }) => {
|
8 |
+
workspace = await Workspace.empty(await browser.newPage(), "graph_creation_spec_test");
|
|
|
|
|
|
|
9 |
await workspace.addBox("NX › Scale-Free Graph");
|
10 |
+
await workspace.getBox("NX › Scale-Free Graph 1").getByLabel("n", { exact: true }).fill("10");
|
|
|
|
|
|
|
11 |
await workspace.addBox("Create graph");
|
12 |
await workspace.connectBoxes("NX › Scale-Free Graph 1", "Create graph 1");
|
13 |
});
|
|
|
39 |
|
40 |
test("Adding and removing relationships", async () => {
|
41 |
const graphBox = await workspace.getBox("Create graph 1");
|
42 |
+
const addRelationshipButton = await graphBox.locator(".add-relationship-button");
|
|
|
|
|
43 |
await addRelationshipButton.click();
|
44 |
const formData: Record<string, string> = {
|
45 |
name: "relation_1",
|
|
|
61 |
// check that the relationship has been saved in the backend
|
62 |
await workspace.page.reload();
|
63 |
const graphBoxAfterReload = await workspace.getBox("Create graph 1");
|
64 |
+
const relationHeader = await graphBoxAfterReload.locator(".graph-relations .df-head", {
|
65 |
+
hasText: "relation_1",
|
66 |
+
});
|
|
|
67 |
await expect(relationHeader).toBeVisible();
|
68 |
await relationHeader.locator("button").click(); // Delete the relationship
|
69 |
await expect(relationHeader).not.toBeVisible();
|