Spaces:
Running
Running
Merge pull request #132 from biggraph/darabos-execute-button
Browse files- .github/workflows/test.yaml +4 -6
- examples/Image processing.lynxkite.json +56 -57
- examples/Model use.lynxkite.json +1 -1
- examples/PyTorch demo.lynxkite.json +0 -623
- examples/requirements.txt +2 -1
- examples/word2vec.py +2 -1
- lynxkite-app/src/lynxkite_app/main.py +8 -0
- lynxkite-app/web/src/Code.tsx +5 -5
- lynxkite-app/web/src/index.css +1 -1
- lynxkite-app/web/src/workspace/Workspace.tsx +21 -6
- lynxkite-app/web/tests/examples.spec.ts +18 -41
- lynxkite-app/web/tests/lynxkite.ts +7 -1
- lynxkite-core/src/lynxkite/core/ops.py +6 -8
- lynxkite-graph-analytics/src/lynxkite_graph_analytics/networkx_ops.py +2 -2
.github/workflows/test.yaml
CHANGED
@@ -8,6 +8,8 @@ on:
|
|
8 |
jobs:
|
9 |
test:
|
10 |
runs-on: ubuntu-latest
|
|
|
|
|
11 |
steps:
|
12 |
- uses: actions/checkout@v4
|
13 |
|
@@ -24,6 +26,8 @@ jobs:
|
|
24 |
run: |
|
25 |
eval `ssh-agent -s`
|
26 |
ssh-add - <<< '${{ secrets.LYNXSCRIBE_DEPLOY_KEY }}'
|
|
|
|
|
27 |
uv pip install \
|
28 |
-e lynxkite-core/[dev] \
|
29 |
-e lynxkite-app/[dev] \
|
@@ -31,15 +35,11 @@ jobs:
|
|
31 |
-e lynxkite-bio \
|
32 |
-e lynxkite-lynxscribe/ \
|
33 |
-e lynxkite-pillow-example/
|
34 |
-
env:
|
35 |
-
UV_SYSTEM_PYTHON: 1
|
36 |
|
37 |
- name: Run pre-commits
|
38 |
run: |
|
39 |
uv pip install pre-commit
|
40 |
pre-commit run --all-files
|
41 |
-
env:
|
42 |
-
UV_SYSTEM_PYTHON: 1
|
43 |
|
44 |
- name: Run core tests
|
45 |
run: |
|
@@ -65,8 +65,6 @@ jobs:
|
|
65 |
run: |
|
66 |
uv pip install mkdocs-material mkdocstrings[python]
|
67 |
mkdocs build
|
68 |
-
env:
|
69 |
-
UV_SYSTEM_PYTHON: 1
|
70 |
|
71 |
- uses: actions/setup-node@v4
|
72 |
with:
|
|
|
8 |
jobs:
|
9 |
test:
|
10 |
runs-on: ubuntu-latest
|
11 |
+
env:
|
12 |
+
UV_SYSTEM_PYTHON: 1
|
13 |
steps:
|
14 |
- uses: actions/checkout@v4
|
15 |
|
|
|
26 |
run: |
|
27 |
eval `ssh-agent -s`
|
28 |
ssh-add - <<< '${{ secrets.LYNXSCRIBE_DEPLOY_KEY }}'
|
29 |
+
uv venv
|
30 |
+
. .venv/bin/activate
|
31 |
uv pip install \
|
32 |
-e lynxkite-core/[dev] \
|
33 |
-e lynxkite-app/[dev] \
|
|
|
35 |
-e lynxkite-bio \
|
36 |
-e lynxkite-lynxscribe/ \
|
37 |
-e lynxkite-pillow-example/
|
|
|
|
|
38 |
|
39 |
- name: Run pre-commits
|
40 |
run: |
|
41 |
uv pip install pre-commit
|
42 |
pre-commit run --all-files
|
|
|
|
|
43 |
|
44 |
- name: Run core tests
|
45 |
run: |
|
|
|
65 |
run: |
|
66 |
uv pip install mkdocs-material mkdocstrings[python]
|
67 |
mkdocs build
|
|
|
|
|
68 |
|
69 |
- uses: actions/setup-node@v4
|
70 |
with:
|
examples/Image processing.lynxkite.json
CHANGED
@@ -8,31 +8,31 @@
|
|
8 |
"targetHandle": "image"
|
9 |
},
|
10 |
{
|
11 |
-
"id": "xy-
|
12 |
-
"source": "
|
13 |
"sourceHandle": "output",
|
14 |
-
"target": "
|
15 |
"targetHandle": "image"
|
16 |
},
|
17 |
{
|
18 |
-
"id": "xy-
|
19 |
-
"source": "
|
20 |
"sourceHandle": "output",
|
21 |
-
"target": "
|
22 |
"targetHandle": "image"
|
23 |
},
|
24 |
{
|
25 |
-
"id": "
|
26 |
-
"source": "
|
27 |
"sourceHandle": "output",
|
28 |
-
"target": "
|
29 |
"targetHandle": "image"
|
30 |
},
|
31 |
{
|
32 |
-
"id": "
|
33 |
-
"source": "
|
34 |
"sourceHandle": "output",
|
35 |
-
"target": "
|
36 |
"targetHandle": "image"
|
37 |
}
|
38 |
],
|
@@ -120,51 +120,6 @@
|
|
120 |
"type": "image",
|
121 |
"width": 265.0
|
122 |
},
|
123 |
-
{
|
124 |
-
"data": {
|
125 |
-
"__execution_delay": null,
|
126 |
-
"collapsed": true,
|
127 |
-
"display": null,
|
128 |
-
"error": null,
|
129 |
-
"input_metadata": null,
|
130 |
-
"meta": {
|
131 |
-
"inputs": {
|
132 |
-
"image": {
|
133 |
-
"name": "image",
|
134 |
-
"position": "left",
|
135 |
-
"type": {
|
136 |
-
"type": "<module 'PIL.Image' from '/media/nvme/darabos/lynxkite-2024/.venv/lib/python3.11/site-packages/PIL/Image.py'>"
|
137 |
-
}
|
138 |
-
}
|
139 |
-
},
|
140 |
-
"name": "Flip verically",
|
141 |
-
"outputs": {
|
142 |
-
"output": {
|
143 |
-
"name": "output",
|
144 |
-
"position": "right",
|
145 |
-
"type": {
|
146 |
-
"type": "None"
|
147 |
-
}
|
148 |
-
}
|
149 |
-
},
|
150 |
-
"params": {},
|
151 |
-
"type": "basic"
|
152 |
-
},
|
153 |
-
"params": {},
|
154 |
-
"status": "done",
|
155 |
-
"title": "Flip verically"
|
156 |
-
},
|
157 |
-
"dragHandle": ".bg-primary",
|
158 |
-
"height": 200.0,
|
159 |
-
"id": "Flip verically 1",
|
160 |
-
"parentId": null,
|
161 |
-
"position": {
|
162 |
-
"x": 228.3853393986406,
|
163 |
-
"y": 245.68255477059915
|
164 |
-
},
|
165 |
-
"type": "basic",
|
166 |
-
"width": 200.0
|
167 |
-
},
|
168 |
{
|
169 |
"data": {
|
170 |
"display": "data:image/jpeg;base64,UklGRoADAABXRUJQVlA4IHQDAAAwNQCdASrIAMgAPm0ukkYyMaGhrtt5UkANiWluu7AObIPJl9NfJmVCwA06FIbr8ZA3WaSJ7/dHH5er4u683qcKWcW7qn4VGE7XCnSEtZUZsarT1ox9y8J2EionbqsxfkAAIOk7JTtvXiLVAeVPQlEW3R4aJSLoxXCJTuGOqcp6fJ7+tzWWmOSq1IqFDN7536TfOvEoiw/K8AQcP06J13RTpwyFow3MFoK9BWZDv0mPqMmHjuC7Hb5MkHc6QnQihSek5PyYdUN4+yG0NpM3NK0+lOSMpu5jaeY5YrFUKke5nLaJ0Mu/3it1akkzebF4fRj8xdIkJDrC4xD4RcyC8aQ1zI4MjffvaCX8X0jbNJ5n5xzGOOU9kFhpry6Uy30xVtGdrEYwbGflOPdNp1u0tiKemkGylZmdj/9rbvWo0IYnCFLs1tAQ1fpkGVQYVo4FA68IbkKvB2KNxrQ17ySt96mzi6ODrNuVzsgisAS/q2ILoOlZL2nfdCNWb523fz7hEM319hb+aWTmo2zExKfWXzUZRGWy09rt8rQZXW5qBKTs5vsYabOjubwNNfFoX7dv32XwAAD++LH6dpCJbVSQouNoBCx0XMzcYw+6o+G//GpIy1eD9A/1cvAp/zhr1mDURDqx2xBgY79tNgsjX9AArhyCNX/BRq/st/uDXRqUv7Wx+Pl/40TsVhng3/XrKZXhDYKnD2aAAyOHG8yS8FTlr5aM6Nh//ra74YvdX+3/j/RLZDm6wRAbHmTF1ZWhK3HdcgOeU+RMXPeCEL8zqEcm3nMeTpuJhH7GTnjejYZx6OrdHAWBghyiaVrgH9f2nXLm1VZ4AgOokXq16NNjRz3FgLYzr7qd94Y2TRJW/cGAcaH5CN5+iXZy4LY2JaCo5I4//pJ/TthQ8SBbQdOIDZPayUXSmAo7cTuD/rk97N3QBGdx5oIzTeSYL8FZfiH/nJLQARX7PafEYZBW9a/ZMjDSPg9GFTPL4BL42F32uL8ZcYLrpkRGRW6jbjmj7GuGSwV5+YuKOhEAf2ZVwU55UjaxRygygrjlYc8FoCe+HyL6rJ5V3jfZvZy1Nw3CdfX4CWQ5/gUjrgONEGb7g2SL/nS9dGgl2gAHKKCzEneCdL38wwsnQYPGdrerT/4uEWM1T24cHLkgBmT+zH6YiwG7DkqEmAbjF4AAAA==",
|
@@ -299,6 +254,50 @@
|
|
299 |
},
|
300 |
"type": "basic",
|
301 |
"width": 200.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
302 |
}
|
303 |
]
|
304 |
}
|
|
|
8 |
"targetHandle": "image"
|
9 |
},
|
10 |
{
|
11 |
+
"id": "xy-edge__To grayscale 1output-View image 2image",
|
12 |
+
"source": "To grayscale 1",
|
13 |
"sourceHandle": "output",
|
14 |
+
"target": "View image 2",
|
15 |
"targetHandle": "image"
|
16 |
},
|
17 |
{
|
18 |
+
"id": "xy-edge__Blur 1output-To grayscale 1image",
|
19 |
+
"source": "Blur 1",
|
20 |
"sourceHandle": "output",
|
21 |
+
"target": "To grayscale 1",
|
22 |
"targetHandle": "image"
|
23 |
},
|
24 |
{
|
25 |
+
"id": "Open image 1 Flip vertically 1",
|
26 |
+
"source": "Open image 1",
|
27 |
"sourceHandle": "output",
|
28 |
+
"target": "Flip vertically 1",
|
29 |
"targetHandle": "image"
|
30 |
},
|
31 |
{
|
32 |
+
"id": "Flip vertically 1 Blur 1",
|
33 |
+
"source": "Flip vertically 1",
|
34 |
"sourceHandle": "output",
|
35 |
+
"target": "Blur 1",
|
36 |
"targetHandle": "image"
|
37 |
}
|
38 |
],
|
|
|
120 |
"type": "image",
|
121 |
"width": 265.0
|
122 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
{
|
124 |
"data": {
|
125 |
"display": "data:image/jpeg;base64,UklGRoADAABXRUJQVlA4IHQDAAAwNQCdASrIAMgAPm0ukkYyMaGhrtt5UkANiWluu7AObIPJl9NfJmVCwA06FIbr8ZA3WaSJ7/dHH5er4u683qcKWcW7qn4VGE7XCnSEtZUZsarT1ox9y8J2EionbqsxfkAAIOk7JTtvXiLVAeVPQlEW3R4aJSLoxXCJTuGOqcp6fJ7+tzWWmOSq1IqFDN7536TfOvEoiw/K8AQcP06J13RTpwyFow3MFoK9BWZDv0mPqMmHjuC7Hb5MkHc6QnQihSek5PyYdUN4+yG0NpM3NK0+lOSMpu5jaeY5YrFUKke5nLaJ0Mu/3it1akkzebF4fRj8xdIkJDrC4xD4RcyC8aQ1zI4MjffvaCX8X0jbNJ5n5xzGOOU9kFhpry6Uy30xVtGdrEYwbGflOPdNp1u0tiKemkGylZmdj/9rbvWo0IYnCFLs1tAQ1fpkGVQYVo4FA68IbkKvB2KNxrQ17ySt96mzi6ODrNuVzsgisAS/q2ILoOlZL2nfdCNWb523fz7hEM319hb+aWTmo2zExKfWXzUZRGWy09rt8rQZXW5qBKTs5vsYabOjubwNNfFoX7dv32XwAAD++LH6dpCJbVSQouNoBCx0XMzcYw+6o+G//GpIy1eD9A/1cvAp/zhr1mDURDqx2xBgY79tNgsjX9AArhyCNX/BRq/st/uDXRqUv7Wx+Pl/40TsVhng3/XrKZXhDYKnD2aAAyOHG8yS8FTlr5aM6Nh//ra74YvdX+3/j/RLZDm6wRAbHmTF1ZWhK3HdcgOeU+RMXPeCEL8zqEcm3nMeTpuJhH7GTnjejYZx6OrdHAWBghyiaVrgH9f2nXLm1VZ4AgOokXq16NNjRz3FgLYzr7qd94Y2TRJW/cGAcaH5CN5+iXZy4LY2JaCo5I4//pJ/TthQ8SBbQdOIDZPayUXSmAo7cTuD/rk97N3QBGdx5oIzTeSYL8FZfiH/nJLQARX7PafEYZBW9a/ZMjDSPg9GFTPL4BL42F32uL8ZcYLrpkRGRW6jbjmj7GuGSwV5+YuKOhEAf2ZVwU55UjaxRygygrjlYc8FoCe+HyL6rJ5V3jfZvZy1Nw3CdfX4CWQ5/gUjrgONEGb7g2SL/nS9dGgl2gAHKKCzEneCdL38wwsnQYPGdrerT/4uEWM1T24cHLkgBmT+zH6YiwG7DkqEmAbjF4AAAA==",
|
|
|
254 |
},
|
255 |
"type": "basic",
|
256 |
"width": 200.0
|
257 |
+
},
|
258 |
+
{
|
259 |
+
"data": {
|
260 |
+
"__execution_delay": null,
|
261 |
+
"collapsed": true,
|
262 |
+
"display": null,
|
263 |
+
"error": null,
|
264 |
+
"input_metadata": null,
|
265 |
+
"meta": {
|
266 |
+
"inputs": {
|
267 |
+
"image": {
|
268 |
+
"name": "image",
|
269 |
+
"position": "left",
|
270 |
+
"type": {
|
271 |
+
"type": "<module 'PIL.Image' from '/media/nvme/darabos/lynxkite-2024/.venv/lib/python3.11/site-packages/PIL/Image.py'>"
|
272 |
+
}
|
273 |
+
}
|
274 |
+
},
|
275 |
+
"name": "Flip vertically",
|
276 |
+
"outputs": {
|
277 |
+
"output": {
|
278 |
+
"name": "output",
|
279 |
+
"position": "right",
|
280 |
+
"type": {
|
281 |
+
"type": "None"
|
282 |
+
}
|
283 |
+
}
|
284 |
+
},
|
285 |
+
"params": {},
|
286 |
+
"type": "basic"
|
287 |
+
},
|
288 |
+
"params": {},
|
289 |
+
"status": "done",
|
290 |
+
"title": "Flip vertically"
|
291 |
+
},
|
292 |
+
"dragHandle": ".bg-primary",
|
293 |
+
"height": 200.0,
|
294 |
+
"id": "Flip vertically 1",
|
295 |
+
"position": {
|
296 |
+
"x": 148.51544517498044,
|
297 |
+
"y": 288.98657171134255
|
298 |
+
},
|
299 |
+
"type": "basic",
|
300 |
+
"width": 200.0
|
301 |
}
|
302 |
]
|
303 |
}
|
examples/Model use.lynxkite.json
CHANGED
@@ -421,7 +421,7 @@
|
|
421 |
"type": "basic"
|
422 |
},
|
423 |
"params": {
|
424 |
-
"model_workspace": "Model definition
|
425 |
"save_as": "model"
|
426 |
},
|
427 |
"status": "done",
|
|
|
421 |
"type": "basic"
|
422 |
},
|
423 |
"params": {
|
424 |
+
"model_workspace": "Model definition",
|
425 |
"save_as": "model"
|
426 |
},
|
427 |
"status": "done",
|
examples/PyTorch demo.lynxkite.json
DELETED
@@ -1,623 +0,0 @@
|
|
1 |
-
{
|
2 |
-
"env": "PyTorch model",
|
3 |
-
"nodes": [
|
4 |
-
{
|
5 |
-
"id": "Input: features 1",
|
6 |
-
"type": "basic",
|
7 |
-
"data": {
|
8 |
-
"title": "Input: features",
|
9 |
-
"params": {},
|
10 |
-
"display": null,
|
11 |
-
"error": null,
|
12 |
-
"meta": {
|
13 |
-
"name": "Input: features",
|
14 |
-
"type": "basic",
|
15 |
-
"outputs": {
|
16 |
-
"x": {
|
17 |
-
"position": "top",
|
18 |
-
"name": "x",
|
19 |
-
"type": {
|
20 |
-
"type": "tensor"
|
21 |
-
}
|
22 |
-
}
|
23 |
-
},
|
24 |
-
"params": {},
|
25 |
-
"inputs": {}
|
26 |
-
},
|
27 |
-
"collapsed": true,
|
28 |
-
"__execution_delay": null
|
29 |
-
},
|
30 |
-
"position": {
|
31 |
-
"x": -108.60604658638658,
|
32 |
-
"y": 63.96065124378427
|
33 |
-
},
|
34 |
-
"width": 200.0,
|
35 |
-
"height": 200.0,
|
36 |
-
"parentId": null
|
37 |
-
},
|
38 |
-
{
|
39 |
-
"id": "Input: graph edges 1",
|
40 |
-
"type": "basic",
|
41 |
-
"data": {
|
42 |
-
"title": "Input: graph edges",
|
43 |
-
"params": {},
|
44 |
-
"display": null,
|
45 |
-
"error": null,
|
46 |
-
"__execution_delay": null,
|
47 |
-
"collapsed": true,
|
48 |
-
"meta": {
|
49 |
-
"name": "Input: graph edges",
|
50 |
-
"inputs": {},
|
51 |
-
"params": {},
|
52 |
-
"type": "basic",
|
53 |
-
"outputs": {
|
54 |
-
"edges": {
|
55 |
-
"name": "edges",
|
56 |
-
"type": {
|
57 |
-
"type": "tensor"
|
58 |
-
},
|
59 |
-
"position": "top"
|
60 |
-
}
|
61 |
-
}
|
62 |
-
}
|
63 |
-
},
|
64 |
-
"position": {
|
65 |
-
"x": 180.7373888617958,
|
66 |
-
"y": 58.54904654355781
|
67 |
-
},
|
68 |
-
"width": 200.0,
|
69 |
-
"parentId": null,
|
70 |
-
"height": 200.0
|
71 |
-
},
|
72 |
-
{
|
73 |
-
"id": "Linear 1",
|
74 |
-
"type": "basic",
|
75 |
-
"data": {
|
76 |
-
"title": "Linear",
|
77 |
-
"params": {
|
78 |
-
"output_dim": "same"
|
79 |
-
},
|
80 |
-
"display": null,
|
81 |
-
"error": null,
|
82 |
-
"meta": {
|
83 |
-
"inputs": {
|
84 |
-
"x": {
|
85 |
-
"type": {
|
86 |
-
"type": "tensor"
|
87 |
-
},
|
88 |
-
"position": "bottom",
|
89 |
-
"name": "x"
|
90 |
-
}
|
91 |
-
},
|
92 |
-
"type": "basic",
|
93 |
-
"name": "Linear",
|
94 |
-
"outputs": {
|
95 |
-
"x": {
|
96 |
-
"type": {
|
97 |
-
"type": "tensor"
|
98 |
-
},
|
99 |
-
"position": "top",
|
100 |
-
"name": "x"
|
101 |
-
}
|
102 |
-
},
|
103 |
-
"params": {
|
104 |
-
"output_dim": {
|
105 |
-
"name": "output_dim",
|
106 |
-
"type": {
|
107 |
-
"type": "<class 'str'>"
|
108 |
-
},
|
109 |
-
"default": "same"
|
110 |
-
}
|
111 |
-
}
|
112 |
-
}
|
113 |
-
},
|
114 |
-
"position": {
|
115 |
-
"x": 78.37881963123723,
|
116 |
-
"y": -528.3012263817914
|
117 |
-
},
|
118 |
-
"parentId": null,
|
119 |
-
"width": 204.0,
|
120 |
-
"height": 140.0
|
121 |
-
},
|
122 |
-
{
|
123 |
-
"id": "Activation 1",
|
124 |
-
"type": "basic",
|
125 |
-
"data": {
|
126 |
-
"title": "Activation",
|
127 |
-
"params": {
|
128 |
-
"type": "ReLU"
|
129 |
-
},
|
130 |
-
"display": null,
|
131 |
-
"error": null,
|
132 |
-
"meta": {
|
133 |
-
"outputs": {
|
134 |
-
"x": {
|
135 |
-
"position": "top",
|
136 |
-
"type": {
|
137 |
-
"type": "tensor"
|
138 |
-
},
|
139 |
-
"name": "x"
|
140 |
-
}
|
141 |
-
},
|
142 |
-
"name": "Activation",
|
143 |
-
"type": "basic",
|
144 |
-
"inputs": {
|
145 |
-
"x": {
|
146 |
-
"position": "bottom",
|
147 |
-
"name": "x",
|
148 |
-
"type": {
|
149 |
-
"type": "tensor"
|
150 |
-
}
|
151 |
-
}
|
152 |
-
},
|
153 |
-
"params": {
|
154 |
-
"type": {
|
155 |
-
"default": "OptionsFor_type.ReLU",
|
156 |
-
"name": "type",
|
157 |
-
"type": {
|
158 |
-
"enum": [
|
159 |
-
"ReLU",
|
160 |
-
"LeakyReLU",
|
161 |
-
"Tanh",
|
162 |
-
"Mish"
|
163 |
-
]
|
164 |
-
}
|
165 |
-
}
|
166 |
-
}
|
167 |
-
}
|
168 |
-
},
|
169 |
-
"position": {
|
170 |
-
"x": 98.44658319023353,
|
171 |
-
"y": -741.1411130550297
|
172 |
-
},
|
173 |
-
"height": 154.0,
|
174 |
-
"parentId": null,
|
175 |
-
"width": 173.0
|
176 |
-
},
|
177 |
-
{
|
178 |
-
"id": "Dropout 1",
|
179 |
-
"type": "basic",
|
180 |
-
"data": {
|
181 |
-
"title": "Dropout",
|
182 |
-
"params": {
|
183 |
-
"p": 0.5
|
184 |
-
},
|
185 |
-
"display": null,
|
186 |
-
"error": null,
|
187 |
-
"meta": {
|
188 |
-
"type": "basic",
|
189 |
-
"inputs": {
|
190 |
-
"x": {
|
191 |
-
"name": "x",
|
192 |
-
"type": {
|
193 |
-
"type": "tensor"
|
194 |
-
},
|
195 |
-
"position": "bottom"
|
196 |
-
}
|
197 |
-
},
|
198 |
-
"name": "Dropout",
|
199 |
-
"params": {
|
200 |
-
"p": {
|
201 |
-
"default": 0.5,
|
202 |
-
"type": {
|
203 |
-
"type": "<class 'float'>"
|
204 |
-
},
|
205 |
-
"name": "p"
|
206 |
-
}
|
207 |
-
},
|
208 |
-
"outputs": {
|
209 |
-
"x": {
|
210 |
-
"position": "top",
|
211 |
-
"name": "x",
|
212 |
-
"type": {
|
213 |
-
"type": "tensor"
|
214 |
-
}
|
215 |
-
}
|
216 |
-
}
|
217 |
-
}
|
218 |
-
},
|
219 |
-
"position": {
|
220 |
-
"x": 73.61437458187964,
|
221 |
-
"y": -929.9824215609918
|
222 |
-
},
|
223 |
-
"width": 207.0,
|
224 |
-
"parentId": null,
|
225 |
-
"height": 159.0
|
226 |
-
},
|
227 |
-
{
|
228 |
-
"id": "Graph conv 1",
|
229 |
-
"type": "basic",
|
230 |
-
"data": {
|
231 |
-
"title": "Graph conv",
|
232 |
-
"params": {
|
233 |
-
"type": "SAGEConv"
|
234 |
-
},
|
235 |
-
"display": null,
|
236 |
-
"error": null,
|
237 |
-
"meta": {
|
238 |
-
"outputs": {
|
239 |
-
"x": {
|
240 |
-
"type": {
|
241 |
-
"type": "tensor"
|
242 |
-
},
|
243 |
-
"name": "x",
|
244 |
-
"position": "top"
|
245 |
-
}
|
246 |
-
},
|
247 |
-
"type": "basic",
|
248 |
-
"name": "Graph conv",
|
249 |
-
"params": {
|
250 |
-
"type": {
|
251 |
-
"name": "type",
|
252 |
-
"default": "OptionsFor_type.GCNConv",
|
253 |
-
"type": {
|
254 |
-
"enum": [
|
255 |
-
"GCNConv",
|
256 |
-
"GATConv",
|
257 |
-
"GATv2Conv",
|
258 |
-
"SAGEConv"
|
259 |
-
]
|
260 |
-
}
|
261 |
-
}
|
262 |
-
},
|
263 |
-
"inputs": {
|
264 |
-
"x": {
|
265 |
-
"type": {
|
266 |
-
"type": "tensor"
|
267 |
-
},
|
268 |
-
"name": "x",
|
269 |
-
"position": "bottom"
|
270 |
-
},
|
271 |
-
"edges": {
|
272 |
-
"name": "edges",
|
273 |
-
"type": {
|
274 |
-
"type": "tensor"
|
275 |
-
},
|
276 |
-
"position": "bottom"
|
277 |
-
}
|
278 |
-
}
|
279 |
-
}
|
280 |
-
},
|
281 |
-
"position": {
|
282 |
-
"x": 64.08886242755246,
|
283 |
-
"y": -269.43023573181557
|
284 |
-
},
|
285 |
-
"height": 200.0,
|
286 |
-
"width": 200.0,
|
287 |
-
"parentId": null
|
288 |
-
},
|
289 |
-
{
|
290 |
-
"id": "Supervised loss 1",
|
291 |
-
"type": "basic",
|
292 |
-
"data": {
|
293 |
-
"title": "Supervised loss",
|
294 |
-
"params": {},
|
295 |
-
"display": null,
|
296 |
-
"error": null,
|
297 |
-
"collapsed": true,
|
298 |
-
"__execution_delay": null,
|
299 |
-
"meta": {
|
300 |
-
"type": "basic",
|
301 |
-
"params": {},
|
302 |
-
"name": "Supervised loss",
|
303 |
-
"inputs": {
|
304 |
-
"y": {
|
305 |
-
"name": "y",
|
306 |
-
"type": {
|
307 |
-
"type": "tensor"
|
308 |
-
},
|
309 |
-
"position": "bottom"
|
310 |
-
},
|
311 |
-
"x": {
|
312 |
-
"name": "x",
|
313 |
-
"type": {
|
314 |
-
"type": "tensor"
|
315 |
-
},
|
316 |
-
"position": "bottom"
|
317 |
-
}
|
318 |
-
},
|
319 |
-
"outputs": {
|
320 |
-
"loss": {
|
321 |
-
"position": "top",
|
322 |
-
"type": {
|
323 |
-
"type": "tensor"
|
324 |
-
},
|
325 |
-
"name": "loss"
|
326 |
-
}
|
327 |
-
}
|
328 |
-
}
|
329 |
-
},
|
330 |
-
"position": {
|
331 |
-
"x": 110.53693593362718,
|
332 |
-
"y": -1123.9976567905628
|
333 |
-
},
|
334 |
-
"parentId": null,
|
335 |
-
"height": 80.0,
|
336 |
-
"width": 204.0
|
337 |
-
},
|
338 |
-
{
|
339 |
-
"id": "Input: label 1",
|
340 |
-
"type": "basic",
|
341 |
-
"data": {
|
342 |
-
"title": "Input: label",
|
343 |
-
"params": {},
|
344 |
-
"display": null,
|
345 |
-
"error": null,
|
346 |
-
"collapsed": true,
|
347 |
-
"__execution_delay": null,
|
348 |
-
"meta": {
|
349 |
-
"inputs": {},
|
350 |
-
"outputs": {
|
351 |
-
"y": {
|
352 |
-
"type": {
|
353 |
-
"type": "tensor"
|
354 |
-
},
|
355 |
-
"position": "top",
|
356 |
-
"name": "y"
|
357 |
-
}
|
358 |
-
},
|
359 |
-
"name": "Input: label",
|
360 |
-
"type": "basic",
|
361 |
-
"params": {}
|
362 |
-
}
|
363 |
-
},
|
364 |
-
"position": {
|
365 |
-
"x": 666.110498676668,
|
366 |
-
"y": -898.6721561114967
|
367 |
-
},
|
368 |
-
"width": 200.0,
|
369 |
-
"height": 73.0,
|
370 |
-
"parentId": null
|
371 |
-
},
|
372 |
-
{
|
373 |
-
"id": "Optimizer 1",
|
374 |
-
"type": "basic",
|
375 |
-
"data": {
|
376 |
-
"title": "Optimizer",
|
377 |
-
"params": {
|
378 |
-
"lr": 0.001,
|
379 |
-
"type": "AdamW"
|
380 |
-
},
|
381 |
-
"display": null,
|
382 |
-
"error": null,
|
383 |
-
"meta": {
|
384 |
-
"name": "Optimizer",
|
385 |
-
"outputs": {},
|
386 |
-
"params": {
|
387 |
-
"lr": {
|
388 |
-
"default": 0.001,
|
389 |
-
"name": "lr",
|
390 |
-
"type": {
|
391 |
-
"type": "<class 'float'>"
|
392 |
-
}
|
393 |
-
},
|
394 |
-
"type": {
|
395 |
-
"type": {
|
396 |
-
"enum": [
|
397 |
-
"AdamW",
|
398 |
-
"Adafactor",
|
399 |
-
"Adagrad",
|
400 |
-
"SGD",
|
401 |
-
"Lion",
|
402 |
-
"Paged AdamW",
|
403 |
-
"Galore AdamW"
|
404 |
-
]
|
405 |
-
},
|
406 |
-
"name": "type",
|
407 |
-
"default": "OptionsFor_type.AdamW"
|
408 |
-
}
|
409 |
-
},
|
410 |
-
"inputs": {
|
411 |
-
"loss": {
|
412 |
-
"type": {
|
413 |
-
"type": "tensor"
|
414 |
-
},
|
415 |
-
"name": "loss",
|
416 |
-
"position": "bottom"
|
417 |
-
}
|
418 |
-
},
|
419 |
-
"type": "basic"
|
420 |
-
}
|
421 |
-
},
|
422 |
-
"position": {
|
423 |
-
"x": 115.25730958703494,
|
424 |
-
"y": -1431.5320892753364
|
425 |
-
},
|
426 |
-
"width": 200.0,
|
427 |
-
"height": 233.0,
|
428 |
-
"parentId": null
|
429 |
-
},
|
430 |
-
{
|
431 |
-
"id": "Repeat 3",
|
432 |
-
"type": "basic",
|
433 |
-
"data": {
|
434 |
-
"title": "Repeat",
|
435 |
-
"params": {
|
436 |
-
"times": 1.0
|
437 |
-
},
|
438 |
-
"display": null,
|
439 |
-
"error": null,
|
440 |
-
"meta": {
|
441 |
-
"type": "basic",
|
442 |
-
"position": {
|
443 |
-
"y": 340.0,
|
444 |
-
"x": 371.0
|
445 |
-
},
|
446 |
-
"outputs": {
|
447 |
-
"output": {
|
448 |
-
"name": "output",
|
449 |
-
"type": {
|
450 |
-
"type": "tensor"
|
451 |
-
},
|
452 |
-
"position": "bottom"
|
453 |
-
}
|
454 |
-
},
|
455 |
-
"name": "Repeat",
|
456 |
-
"params": {
|
457 |
-
"times": {
|
458 |
-
"name": "times",
|
459 |
-
"default": 1.0,
|
460 |
-
"type": {
|
461 |
-
"type": "<class 'int'>"
|
462 |
-
}
|
463 |
-
}
|
464 |
-
},
|
465 |
-
"inputs": {
|
466 |
-
"input": {
|
467 |
-
"type": {
|
468 |
-
"type": "tensor"
|
469 |
-
},
|
470 |
-
"position": "top",
|
471 |
-
"name": "input"
|
472 |
-
}
|
473 |
-
}
|
474 |
-
}
|
475 |
-
},
|
476 |
-
"position": {
|
477 |
-
"x": -245.1288628776232,
|
478 |
-
"y": -276.90661040974317
|
479 |
-
},
|
480 |
-
"width": 200.0,
|
481 |
-
"height": 200.0
|
482 |
-
},
|
483 |
-
{
|
484 |
-
"id": "Repeat 1",
|
485 |
-
"type": "basic",
|
486 |
-
"data": {
|
487 |
-
"title": "Repeat",
|
488 |
-
"params": {
|
489 |
-
"times": 1.0
|
490 |
-
},
|
491 |
-
"display": null,
|
492 |
-
"error": null,
|
493 |
-
"meta": {
|
494 |
-
"outputs": {
|
495 |
-
"output": {
|
496 |
-
"position": "bottom",
|
497 |
-
"type": {
|
498 |
-
"type": "tensor"
|
499 |
-
},
|
500 |
-
"name": "output"
|
501 |
-
}
|
502 |
-
},
|
503 |
-
"params": {
|
504 |
-
"times": {
|
505 |
-
"default": 1.0,
|
506 |
-
"type": {
|
507 |
-
"type": "<class 'int'>"
|
508 |
-
},
|
509 |
-
"name": "times"
|
510 |
-
}
|
511 |
-
},
|
512 |
-
"position": {
|
513 |
-
"x": 387.0,
|
514 |
-
"y": 337.0
|
515 |
-
},
|
516 |
-
"type": "basic",
|
517 |
-
"name": "Repeat",
|
518 |
-
"inputs": {
|
519 |
-
"input": {
|
520 |
-
"name": "input",
|
521 |
-
"position": "top",
|
522 |
-
"type": {
|
523 |
-
"type": "tensor"
|
524 |
-
}
|
525 |
-
}
|
526 |
-
}
|
527 |
-
}
|
528 |
-
},
|
529 |
-
"position": {
|
530 |
-
"x": -258.0088683218416,
|
531 |
-
"y": -737.3822225246788
|
532 |
-
},
|
533 |
-
"width": 200.0,
|
534 |
-
"height": 200.0
|
535 |
-
}
|
536 |
-
],
|
537 |
-
"edges": [
|
538 |
-
{
|
539 |
-
"id": "xy-edge__Linear 1x-Activation 1x",
|
540 |
-
"source": "Linear 1",
|
541 |
-
"target": "Activation 1",
|
542 |
-
"sourceHandle": "x",
|
543 |
-
"targetHandle": "x"
|
544 |
-
},
|
545 |
-
{
|
546 |
-
"id": "xy-edge__Activation 1x-Dropout 1x",
|
547 |
-
"source": "Activation 1",
|
548 |
-
"target": "Dropout 1",
|
549 |
-
"sourceHandle": "x",
|
550 |
-
"targetHandle": "x"
|
551 |
-
},
|
552 |
-
{
|
553 |
-
"id": "xy-edge__Input: features 1x-Graph conv 1x",
|
554 |
-
"source": "Input: features 1",
|
555 |
-
"target": "Graph conv 1",
|
556 |
-
"sourceHandle": "x",
|
557 |
-
"targetHandle": "x"
|
558 |
-
},
|
559 |
-
{
|
560 |
-
"id": "xy-edge__Input: graph edges 1edges-Graph conv 1edges",
|
561 |
-
"source": "Input: graph edges 1",
|
562 |
-
"target": "Graph conv 1",
|
563 |
-
"sourceHandle": "edges",
|
564 |
-
"targetHandle": "edges"
|
565 |
-
},
|
566 |
-
{
|
567 |
-
"id": "xy-edge__Graph conv 1x-Linear 1x",
|
568 |
-
"source": "Graph conv 1",
|
569 |
-
"target": "Linear 1",
|
570 |
-
"sourceHandle": "x",
|
571 |
-
"targetHandle": "x"
|
572 |
-
},
|
573 |
-
{
|
574 |
-
"id": "xy-edge__Input: label 1y-Supervised loss 1y",
|
575 |
-
"source": "Input: label 1",
|
576 |
-
"target": "Supervised loss 1",
|
577 |
-
"sourceHandle": "y",
|
578 |
-
"targetHandle": "y"
|
579 |
-
},
|
580 |
-
{
|
581 |
-
"id": "xy-edge__Dropout 1x-Supervised loss 1x",
|
582 |
-
"source": "Dropout 1",
|
583 |
-
"target": "Supervised loss 1",
|
584 |
-
"sourceHandle": "x",
|
585 |
-
"targetHandle": "x"
|
586 |
-
},
|
587 |
-
{
|
588 |
-
"id": "xy-edge__Supervised loss 1loss-Optimizer 1loss",
|
589 |
-
"source": "Supervised loss 1",
|
590 |
-
"target": "Optimizer 1",
|
591 |
-
"sourceHandle": "loss",
|
592 |
-
"targetHandle": "loss"
|
593 |
-
},
|
594 |
-
{
|
595 |
-
"id": "Graph conv 1 Repeat 3",
|
596 |
-
"source": "Graph conv 1",
|
597 |
-
"target": "Repeat 3",
|
598 |
-
"sourceHandle": "x",
|
599 |
-
"targetHandle": "input"
|
600 |
-
},
|
601 |
-
{
|
602 |
-
"id": "Repeat 3 Graph conv 1",
|
603 |
-
"source": "Repeat 3",
|
604 |
-
"target": "Graph conv 1",
|
605 |
-
"sourceHandle": "output",
|
606 |
-
"targetHandle": "x"
|
607 |
-
},
|
608 |
-
{
|
609 |
-
"id": "Dropout 1 Repeat 1",
|
610 |
-
"source": "Dropout 1",
|
611 |
-
"target": "Repeat 1",
|
612 |
-
"sourceHandle": "x",
|
613 |
-
"targetHandle": "input"
|
614 |
-
},
|
615 |
-
{
|
616 |
-
"id": "Repeat 1 Linear 1",
|
617 |
-
"source": "Repeat 1",
|
618 |
-
"target": "Linear 1",
|
619 |
-
"sourceHandle": "output",
|
620 |
-
"targetHandle": "x"
|
621 |
-
}
|
622 |
-
]
|
623 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/requirements.txt
CHANGED
@@ -1 +1,2 @@
|
|
1 |
-
|
|
|
|
1 |
+
# Example of a requirements.txt file. LynxKite will automatically install anything you put here.
|
2 |
+
faker
|
examples/word2vec.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
from lynxkite.core.ops import op
|
2 |
-
import staticvectors
|
3 |
import pandas as pd
|
4 |
|
5 |
ENV = "LynxKite Graph Analytics"
|
@@ -7,6 +6,8 @@ ENV = "LynxKite Graph Analytics"
|
|
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",
|
|
|
1 |
from lynxkite.core.ops import op
|
|
|
2 |
import pandas as pd
|
3 |
|
4 |
ENV = "LynxKite Graph Analytics"
|
|
|
6 |
|
7 |
@op(ENV, "Word2vec for the top 1000 words", slow=True)
|
8 |
def word2vec_1000():
|
9 |
+
import staticvectors
|
10 |
+
|
11 |
model = staticvectors.StaticVectors("neuml/word2vec-quantized")
|
12 |
df = pd.read_csv(
|
13 |
"https://gist.githubusercontent.com/deekayen/4148741/raw/98d35708fa344717d8eee15d11987de6c8e26d7d/1-1000.txt",
|
lynxkite-app/src/lynxkite_app/main.py
CHANGED
@@ -151,6 +151,14 @@ async def upload(req: fastapi.Request):
|
|
151 |
return {"status": "ok"}
|
152 |
|
153 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
class SPAStaticFiles(StaticFiles):
|
155 |
"""Route everything to index.html. https://stackoverflow.com/a/73552966/3318517"""
|
156 |
|
|
|
151 |
return {"status": "ok"}
|
152 |
|
153 |
|
154 |
+
@app.post("/api/execute_workspace")
|
155 |
+
async def execute_workspace(name: str):
|
156 |
+
"""Trigger and await the execution of a workspace."""
|
157 |
+
room = await crdt.ws_websocket_server.get_room(name)
|
158 |
+
ws_pyd = workspace.Workspace.model_validate(room.ws.to_py())
|
159 |
+
await crdt.execute(name, room.ws, ws_pyd)
|
160 |
+
|
161 |
+
|
162 |
class SPAStaticFiles(StaticFiles):
|
163 |
"""Route everything to index.html. https://stackoverflow.com/a/73552966/3318517"""
|
164 |
|
lynxkite-app/web/src/Code.tsx
CHANGED
@@ -71,13 +71,13 @@ export default function Code() {
|
|
71 |
</a>
|
72 |
<div className="ws-name">{path}</div>
|
73 |
<div className="tools text-secondary">
|
74 |
-
<
|
75 |
<Atom />
|
76 |
-
</
|
77 |
-
<
|
78 |
<Backspace />
|
79 |
-
</
|
80 |
-
<a href={`/dir/${parentDir}`}>
|
81 |
<Close />
|
82 |
</a>
|
83 |
</div>
|
|
|
71 |
</a>
|
72 |
<div className="ws-name">{path}</div>
|
73 |
<div className="tools text-secondary">
|
74 |
+
<button className="btn btn-link">
|
75 |
<Atom />
|
76 |
+
</button>
|
77 |
+
<button className="btn btn-link">
|
78 |
<Backspace />
|
79 |
+
</button>
|
80 |
+
<a href={`/dir/${parentDir}`} className="btn btn-link">
|
81 |
<Close />
|
82 |
</a>
|
83 |
</div>
|
lynxkite-app/web/src/index.css
CHANGED
@@ -54,7 +54,7 @@ body {
|
|
54 |
display: flex;
|
55 |
align-items: center;
|
56 |
|
57 |
-
|
58 |
color: oklch(75% 0.13 230);
|
59 |
font-size: 1.5em;
|
60 |
padding: 0 10px;
|
|
|
54 |
display: flex;
|
55 |
align-items: center;
|
56 |
|
57 |
+
.btn {
|
58 |
color: oklch(75% 0.13 230);
|
59 |
font-size: 1.5em;
|
60 |
padding: 0 10px;
|
lynxkite-app/web/src/workspace/Workspace.tsx
CHANGED
@@ -26,6 +26,8 @@ import Atom from "~icons/tabler/atom.jsx";
|
|
26 |
// @ts-ignore
|
27 |
import Backspace from "~icons/tabler/backspace.jsx";
|
28 |
// @ts-ignore
|
|
|
|
|
29 |
import Close from "~icons/tabler/x.jsx";
|
30 |
import type { Workspace, WorkspaceNode } from "../apiTypes.ts";
|
31 |
import favicon from "../assets/favicon.ico";
|
@@ -181,12 +183,16 @@ function LynxKiteFlow() {
|
|
181 |
useEffect(() => {
|
182 |
const handleKeyDown = (event: KeyboardEvent) => {
|
183 |
// Show the node search dialog on "/".
|
184 |
-
if (
|
|
|
185 |
event.preventDefault();
|
186 |
setNodeSearchSettings({
|
187 |
pos: { x: 100, y: 100 },
|
188 |
boxes: catalog.data![state.workspace.env!],
|
189 |
});
|
|
|
|
|
|
|
190 |
}
|
191 |
};
|
192 |
// TODO: Switch to keydown once https://github.com/xyflow/xyflow/pull/5055 is merged.
|
@@ -318,6 +324,12 @@ function LynxKiteFlow() {
|
|
318 |
setMessage("File upload failed.");
|
319 |
}
|
320 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
321 |
return (
|
322 |
<div className="workspace">
|
323 |
<div className="top-bar bg-neutral">
|
@@ -334,13 +346,16 @@ function LynxKiteFlow() {
|
|
334 |
}}
|
335 |
/>
|
336 |
<div className="tools text-secondary">
|
337 |
-
<
|
338 |
<Atom />
|
339 |
-
</
|
340 |
-
<
|
341 |
<Backspace />
|
342 |
-
</
|
343 |
-
<
|
|
|
|
|
|
|
344 |
<Close />
|
345 |
</a>
|
346 |
</div>
|
|
|
26 |
// @ts-ignore
|
27 |
import Backspace from "~icons/tabler/backspace.jsx";
|
28 |
// @ts-ignore
|
29 |
+
import Restart from "~icons/tabler/rotate-clockwise.jsx";
|
30 |
+
// @ts-ignore
|
31 |
import Close from "~icons/tabler/x.jsx";
|
32 |
import type { Workspace, WorkspaceNode } from "../apiTypes.ts";
|
33 |
import favicon from "../assets/favicon.ico";
|
|
|
183 |
useEffect(() => {
|
184 |
const handleKeyDown = (event: KeyboardEvent) => {
|
185 |
// Show the node search dialog on "/".
|
186 |
+
if (nodeSearchSettings || isTypingInFormElement()) return;
|
187 |
+
if (event.key === "/") {
|
188 |
event.preventDefault();
|
189 |
setNodeSearchSettings({
|
190 |
pos: { x: 100, y: 100 },
|
191 |
boxes: catalog.data![state.workspace.env!],
|
192 |
});
|
193 |
+
} else if (event.key === "r") {
|
194 |
+
event.preventDefault();
|
195 |
+
executeWorkspace();
|
196 |
}
|
197 |
};
|
198 |
// TODO: Switch to keydown once https://github.com/xyflow/xyflow/pull/5055 is merged.
|
|
|
324 |
setMessage("File upload failed.");
|
325 |
}
|
326 |
}
|
327 |
+
async function executeWorkspace() {
|
328 |
+
const response = await axios.post(`/api/execute_workspace?name=${path}`);
|
329 |
+
if (response.status !== 200) {
|
330 |
+
setMessage("Workspace execution failed.");
|
331 |
+
}
|
332 |
+
}
|
333 |
return (
|
334 |
<div className="workspace">
|
335 |
<div className="top-bar bg-neutral">
|
|
|
346 |
}}
|
347 |
/>
|
348 |
<div className="tools text-secondary">
|
349 |
+
<button className="btn btn-link">
|
350 |
<Atom />
|
351 |
+
</button>
|
352 |
+
<button className="btn btn-link">
|
353 |
<Backspace />
|
354 |
+
</button>
|
355 |
+
<button className="btn btn-link" onClick={executeWorkspace}>
|
356 |
+
<Restart />
|
357 |
+
</button>
|
358 |
+
<a className="btn btn-link" href={`/dir/${parentDir}`}>
|
359 |
<Close />
|
360 |
</a>
|
361 |
</div>
|
lynxkite-app/web/tests/examples.spec.ts
CHANGED
@@ -2,44 +2,21 @@
|
|
2 |
import { expect, test } from "@playwright/test";
|
3 |
import { Workspace } from "./lynxkite";
|
4 |
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
});
|
24 |
-
|
25 |
-
test("LynxScribe example", async ({ page }) => {
|
26 |
-
// Fails because of missing OPENAI_API_KEY
|
27 |
-
const ws = await Workspace.open(page, "LynxScribe demo");
|
28 |
-
await ws.expectErrorFree();
|
29 |
-
});
|
30 |
-
|
31 |
-
test("Graph RAG", async ({ page }) => {
|
32 |
-
// Fails due to some issue with ChromaDB
|
33 |
-
const ws = await Workspace.open(page, "Graph RAG");
|
34 |
-
await ws.expectErrorFree(process.env.CI ? 2000 : 500);
|
35 |
-
});
|
36 |
-
|
37 |
-
test("Airlines demo", async ({ page }) => {
|
38 |
-
const ws = await Workspace.open(page, "Airlines demo");
|
39 |
-
await ws.expectErrorFree(process.env.CI ? 10000 : 500);
|
40 |
-
});
|
41 |
-
|
42 |
-
test("Pillow example", async ({ page }) => {
|
43 |
-
const ws = await Workspace.open(page, "Image processing");
|
44 |
-
await ws.expectErrorFree();
|
45 |
-
});
|
|
|
2 |
import { expect, test } from "@playwright/test";
|
3 |
import { Workspace } from "./lynxkite";
|
4 |
|
5 |
+
const WORKSPACES = [
|
6 |
+
// "AIMO",
|
7 |
+
"Airlines demo",
|
8 |
+
"Bio Cypher demo",
|
9 |
+
// "Graph RAG",
|
10 |
+
"Image processing",
|
11 |
+
// "LynxScribe demo",
|
12 |
+
"NetworkX demo",
|
13 |
+
"Model use",
|
14 |
+
];
|
15 |
+
|
16 |
+
for (const name of WORKSPACES) {
|
17 |
+
test(name, async ({ page }) => {
|
18 |
+
const ws = await Workspace.open(page, name);
|
19 |
+
await ws.execute();
|
20 |
+
await ws.expectErrorFree();
|
21 |
+
});
|
22 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lynxkite-app/web/tests/lynxkite.ts
CHANGED
@@ -144,8 +144,14 @@ export class Workspace {
|
|
144 |
await this.page.mouse.up();
|
145 |
}
|
146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
async expectErrorFree(executionWaitTime?) {
|
148 |
-
await expect(this.getBoxes().locator("
|
149 |
}
|
150 |
|
151 |
async close() {
|
|
|
144 |
await this.page.mouse.up();
|
145 |
}
|
146 |
|
147 |
+
async execute() {
|
148 |
+
const request = this.page.waitForResponse(/api[/]execute_workspace/);
|
149 |
+
await this.page.keyboard.press("r");
|
150 |
+
await request;
|
151 |
+
}
|
152 |
+
|
153 |
async expectErrorFree(executionWaitTime?) {
|
154 |
+
await expect(this.getBoxes().locator("text=⚠️").first()).not.toBeVisible();
|
155 |
}
|
156 |
|
157 |
async close() {
|
lynxkite-core/src/lynxkite/core/ops.py
CHANGED
@@ -344,29 +344,27 @@ def load_user_scripts(workspace: str):
|
|
344 |
assert path.is_relative_to(cwd), "Provided workspace path is invalid"
|
345 |
for p in path.parents:
|
346 |
print("checking user scripts in", p)
|
347 |
-
for f in p.glob("*.py"):
|
348 |
-
try:
|
349 |
-
run_user_script(f)
|
350 |
-
except Exception:
|
351 |
-
traceback.print_exc()
|
352 |
req = p / "requirements.txt"
|
353 |
if req.exists():
|
354 |
try:
|
355 |
install_requirements(req)
|
356 |
except Exception:
|
357 |
traceback.print_exc()
|
|
|
|
|
|
|
|
|
|
|
358 |
if p == cwd:
|
359 |
break
|
360 |
|
361 |
|
362 |
def install_requirements(req: pathlib.Path):
|
363 |
-
cmd = ["uv", "pip", "install", "-r", str(req)]
|
364 |
-
print(f"Running {' '.join(cmd)}")
|
365 |
subprocess.check_call(cmd)
|
366 |
|
367 |
|
368 |
def run_user_script(script_path: pathlib.Path):
|
369 |
-
print(f"Running {script_path}...")
|
370 |
spec = importlib.util.spec_from_file_location(script_path.stem, str(script_path))
|
371 |
module = importlib.util.module_from_spec(spec)
|
372 |
spec.loader.exec_module(module)
|
|
|
344 |
assert path.is_relative_to(cwd), "Provided workspace path is invalid"
|
345 |
for p in path.parents:
|
346 |
print("checking user scripts in", p)
|
|
|
|
|
|
|
|
|
|
|
347 |
req = p / "requirements.txt"
|
348 |
if req.exists():
|
349 |
try:
|
350 |
install_requirements(req)
|
351 |
except Exception:
|
352 |
traceback.print_exc()
|
353 |
+
for f in p.glob("*.py"):
|
354 |
+
try:
|
355 |
+
run_user_script(f)
|
356 |
+
except Exception:
|
357 |
+
traceback.print_exc()
|
358 |
if p == cwd:
|
359 |
break
|
360 |
|
361 |
|
362 |
def install_requirements(req: pathlib.Path):
|
363 |
+
cmd = ["uv", "pip", "install", "-q", "-r", str(req)]
|
|
|
364 |
subprocess.check_call(cmd)
|
365 |
|
366 |
|
367 |
def run_user_script(script_path: pathlib.Path):
|
|
|
368 |
spec = importlib.util.spec_from_file_location(script_path.stem, str(script_path))
|
369 |
module = importlib.util.module_from_spec(spec)
|
370 |
spec.loader.exec_module(module)
|
lynxkite-graph-analytics/src/lynxkite_graph_analytics/networkx_ops.py
CHANGED
@@ -152,11 +152,11 @@ def types_from_doc(doc: str) -> dict[str, type]:
|
|
152 |
|
153 |
def wrapped(name: str, func):
|
154 |
@functools.wraps(func)
|
155 |
-
def wrapper(*args, **kwargs):
|
156 |
for k, v in kwargs.items():
|
157 |
if v == "None":
|
158 |
kwargs[k] = None
|
159 |
-
res = func(*args, **kwargs)
|
160 |
# Figure out what the returned value is.
|
161 |
if isinstance(res, nx.Graph):
|
162 |
return res
|
|
|
152 |
|
153 |
def wrapped(name: str, func):
|
154 |
@functools.wraps(func)
|
155 |
+
async def wrapper(*args, **kwargs):
|
156 |
for k, v in kwargs.items():
|
157 |
if v == "None":
|
158 |
kwargs[k] = None
|
159 |
+
res = await ops.slow(func)(*args, **kwargs)
|
160 |
# Figure out what the returned value is.
|
161 |
if isinstance(res, nx.Graph):
|
162 |
return res
|