Spaces:
Running
Running
Merge pull request #45 from biggraph/darabos-shattering
Browse filesThis view is limited to 50 files because it contains too many changes. Β
See raw diff
- .gitignore +4 -1
- LICENSE.txt +0 -201
- README.md +19 -10
- lynxkite-app/.gitignore +3 -0
- lynxkite-app/MANIFEST.in +2 -0
- lynxkite-app/README.md +25 -0
- {data β lynxkite-app/data}/Image processing +0 -0
- {data β lynxkite-app/data}/NetworkX demo +0 -0
- {data β lynxkite-app/data}/PyTorch demo +0 -0
- {data β lynxkite-app/data}/sql +0 -0
- lynxkite-app/pyproject.toml +39 -0
- lynxkite-app/src/build_frontend.py +26 -0
- {server β lynxkite-app/src/lynxkite/app}/__init__.py +0 -0
- lynxkite-app/src/lynxkite/app/__main__.py +13 -0
- {server β lynxkite-app/src/lynxkite/app}/crdt.py +5 -9
- {server β lynxkite-app/src/lynxkite/app}/main.py +44 -10
- lynxkite-app/src/lynxkite/app/web_assets/__init__.py +1 -0
- lynxkite-app/src/lynxkite/app/web_assets/assets/__init__.py +1 -0
- lynxkite-app/uv.lock +0 -0
- {web β lynxkite-app/web}/.gitignore +0 -0
- {web β lynxkite-app/web}/README.md +0 -0
- {web β lynxkite-app/web}/eslint.config.js +0 -0
- {web β lynxkite-app/web}/index.html +0 -0
- {web β lynxkite-app/web}/package-lock.json +0 -0
- {web β lynxkite-app/web}/package.json +4 -4
- {web β lynxkite-app/web}/postcss.config.js +0 -0
- {web β lynxkite-app/web}/src/Directory.tsx +0 -0
- {web β lynxkite-app/web}/src/apiTypes.ts +0 -0
- {web β lynxkite-app/web}/src/assets/favicon.ico +0 -0
- {web β lynxkite-app/web}/src/assets/logo.png +0 -0
- {web β lynxkite-app/web}/src/index.css +0 -0
- {web β lynxkite-app/web}/src/main.tsx +0 -0
- {web β lynxkite-app/web}/src/vite-env.d.ts +0 -0
- {web β lynxkite-app/web}/src/workspace/EnvironmentSelector.tsx +0 -0
- {web β lynxkite-app/web}/src/workspace/LynxKiteState.ts +0 -0
- {web β lynxkite-app/web}/src/workspace/NodeSearch.tsx +0 -0
- {web β lynxkite-app/web}/src/workspace/Workspace.tsx +1 -1
- {web β lynxkite-app/web}/src/workspace/nodes/LynxKiteNode.tsx +0 -0
- {web β lynxkite-app/web}/src/workspace/nodes/NodeParameter.tsx +10 -3
- {web β lynxkite-app/web}/src/workspace/nodes/NodeWithImage.tsx +0 -0
- {web β lynxkite-app/web}/src/workspace/nodes/NodeWithParams.tsx +0 -0
- {web β lynxkite-app/web}/src/workspace/nodes/NodeWithTableView.tsx +0 -0
- {web β lynxkite-app/web}/src/workspace/nodes/NodeWithVisualization.tsx +1 -1
- {web β lynxkite-app/web}/src/workspace/nodes/Table.tsx +0 -0
- {web β lynxkite-app/web}/tailwind.config.js +1 -0
- {web β lynxkite-app/web}/tsconfig.app.json +0 -0
- {web β lynxkite-app/web}/tsconfig.json +0 -0
- {web β lynxkite-app/web}/tsconfig.node.json +0 -0
- {web β lynxkite-app/web}/vite.config.ts +9 -0
- lynxkite-core/README.md +4 -0
.gitignore
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
# Editor directories and files
|
2 |
.vscode/*
|
3 |
!.vscode/extensions.json
|
4 |
.idea
|
@@ -9,3 +8,7 @@
|
|
9 |
*.sln
|
10 |
*.sw?
|
11 |
__pycache__
|
|
|
|
|
|
|
|
|
|
|
|
1 |
.vscode/*
|
2 |
!.vscode/extensions.json
|
3 |
.idea
|
|
|
8 |
*.sln
|
9 |
*.sw?
|
10 |
__pycache__
|
11 |
+
node_modules
|
12 |
+
dist
|
13 |
+
build
|
14 |
+
*.egg-info
|
LICENSE.txt
DELETED
@@ -1,201 +0,0 @@
|
|
1 |
-
Apache License
|
2 |
-
Version 2.0, January 2004
|
3 |
-
http://www.apache.org/licenses/
|
4 |
-
|
5 |
-
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
6 |
-
|
7 |
-
1. Definitions.
|
8 |
-
|
9 |
-
"License" shall mean the terms and conditions for use, reproduction,
|
10 |
-
and distribution as defined by Sections 1 through 9 of this document.
|
11 |
-
|
12 |
-
"Licensor" shall mean the copyright owner or entity authorized by
|
13 |
-
the copyright owner that is granting the License.
|
14 |
-
|
15 |
-
"Legal Entity" shall mean the union of the acting entity and all
|
16 |
-
other entities that control, are controlled by, or are under common
|
17 |
-
control with that entity. For the purposes of this definition,
|
18 |
-
"control" means (i) the power, direct or indirect, to cause the
|
19 |
-
direction or management of such entity, whether by contract or
|
20 |
-
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
21 |
-
outstanding shares, or (iii) beneficial ownership of such entity.
|
22 |
-
|
23 |
-
"You" (or "Your") shall mean an individual or Legal Entity
|
24 |
-
exercising permissions granted by this License.
|
25 |
-
|
26 |
-
"Source" form shall mean the preferred form for making modifications,
|
27 |
-
including but not limited to software source code, documentation
|
28 |
-
source, and configuration files.
|
29 |
-
|
30 |
-
"Object" form shall mean any form resulting from mechanical
|
31 |
-
transformation or translation of a Source form, including but
|
32 |
-
not limited to compiled object code, generated documentation,
|
33 |
-
and conversions to other media types.
|
34 |
-
|
35 |
-
"Work" shall mean the work of authorship, whether in Source or
|
36 |
-
Object form, made available under the License, as indicated by a
|
37 |
-
copyright notice that is included in or attached to the work
|
38 |
-
(an example is provided in the Appendix below).
|
39 |
-
|
40 |
-
"Derivative Works" shall mean any work, whether in Source or Object
|
41 |
-
form, that is based on (or derived from) the Work and for which the
|
42 |
-
editorial revisions, annotations, elaborations, or other modifications
|
43 |
-
represent, as a whole, an original work of authorship. For the purposes
|
44 |
-
of this License, Derivative Works shall not include works that remain
|
45 |
-
separable from, or merely link (or bind by name) to the interfaces of,
|
46 |
-
the Work and Derivative Works thereof.
|
47 |
-
|
48 |
-
"Contribution" shall mean any work of authorship, including
|
49 |
-
the original version of the Work and any modifications or additions
|
50 |
-
to that Work or Derivative Works thereof, that is intentionally
|
51 |
-
submitted to Licensor for inclusion in the Work by the copyright owner
|
52 |
-
or by an individual or Legal Entity authorized to submit on behalf of
|
53 |
-
the copyright owner. For the purposes of this definition, "submitted"
|
54 |
-
means any form of electronic, verbal, or written communication sent
|
55 |
-
to the Licensor or its representatives, including but not limited to
|
56 |
-
communication on electronic mailing lists, source code control systems,
|
57 |
-
and issue tracking systems that are managed by, or on behalf of, the
|
58 |
-
Licensor for the purpose of discussing and improving the Work, but
|
59 |
-
excluding communication that is conspicuously marked or otherwise
|
60 |
-
designated in writing by the copyright owner as "Not a Contribution."
|
61 |
-
|
62 |
-
"Contributor" shall mean Licensor and any individual or Legal Entity
|
63 |
-
on behalf of whom a Contribution has been received by Licensor and
|
64 |
-
subsequently incorporated within the Work.
|
65 |
-
|
66 |
-
2. Grant of Copyright License. Subject to the terms and conditions of
|
67 |
-
this License, each Contributor hereby grants to You a perpetual,
|
68 |
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
69 |
-
copyright license to reproduce, prepare Derivative Works of,
|
70 |
-
publicly display, publicly perform, sublicense, and distribute the
|
71 |
-
Work and such Derivative Works in Source or Object form.
|
72 |
-
|
73 |
-
3. Grant of Patent License. Subject to the terms and conditions of
|
74 |
-
this License, each Contributor hereby grants to You a perpetual,
|
75 |
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
76 |
-
(except as stated in this section) patent license to make, have made,
|
77 |
-
use, offer to sell, sell, import, and otherwise transfer the Work,
|
78 |
-
where such license applies only to those patent claims licensable
|
79 |
-
by such Contributor that are necessarily infringed by their
|
80 |
-
Contribution(s) alone or by combination of their Contribution(s)
|
81 |
-
with the Work to which such Contribution(s) was submitted. If You
|
82 |
-
institute patent litigation against any entity (including a
|
83 |
-
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
84 |
-
or a Contribution incorporated within the Work constitutes direct
|
85 |
-
or contributory patent infringement, then any patent licenses
|
86 |
-
granted to You under this License for that Work shall terminate
|
87 |
-
as of the date such litigation is filed.
|
88 |
-
|
89 |
-
4. Redistribution. You may reproduce and distribute copies of the
|
90 |
-
Work or Derivative Works thereof in any medium, with or without
|
91 |
-
modifications, and in Source or Object form, provided that You
|
92 |
-
meet the following conditions:
|
93 |
-
|
94 |
-
(a) You must give any other recipients of the Work or
|
95 |
-
Derivative Works a copy of this License; and
|
96 |
-
|
97 |
-
(b) You must cause any modified files to carry prominent notices
|
98 |
-
stating that You changed the files; and
|
99 |
-
|
100 |
-
(c) You must retain, in the Source form of any Derivative Works
|
101 |
-
that You distribute, all copyright, patent, trademark, and
|
102 |
-
attribution notices from the Source form of the Work,
|
103 |
-
excluding those notices that do not pertain to any part of
|
104 |
-
the Derivative Works; and
|
105 |
-
|
106 |
-
(d) If the Work includes a "NOTICE" text file as part of its
|
107 |
-
distribution, then any Derivative Works that You distribute must
|
108 |
-
include a readable copy of the attribution notices contained
|
109 |
-
within such NOTICE file, excluding those notices that do not
|
110 |
-
pertain to any part of the Derivative Works, in at least one
|
111 |
-
of the following places: within a NOTICE text file distributed
|
112 |
-
as part of the Derivative Works; within the Source form or
|
113 |
-
documentation, if provided along with the Derivative Works; or,
|
114 |
-
within a display generated by the Derivative Works, if and
|
115 |
-
wherever such third-party notices normally appear. The contents
|
116 |
-
of the NOTICE file are for informational purposes only and
|
117 |
-
do not modify the License. You may add Your own attribution
|
118 |
-
notices within Derivative Works that You distribute, alongside
|
119 |
-
or as an addendum to the NOTICE text from the Work, provided
|
120 |
-
that such additional attribution notices cannot be construed
|
121 |
-
as modifying the License.
|
122 |
-
|
123 |
-
You may add Your own copyright statement to Your modifications and
|
124 |
-
may provide additional or different license terms and conditions
|
125 |
-
for use, reproduction, or distribution of Your modifications, or
|
126 |
-
for any such Derivative Works as a whole, provided Your use,
|
127 |
-
reproduction, and distribution of the Work otherwise complies with
|
128 |
-
the conditions stated in this License.
|
129 |
-
|
130 |
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
131 |
-
any Contribution intentionally submitted for inclusion in the Work
|
132 |
-
by You to the Licensor shall be under the terms and conditions of
|
133 |
-
this License, without any additional terms or conditions.
|
134 |
-
Notwithstanding the above, nothing herein shall supersede or modify
|
135 |
-
the terms of any separate license agreement you may have executed
|
136 |
-
with Licensor regarding such Contributions.
|
137 |
-
|
138 |
-
6. Trademarks. This License does not grant permission to use the trade
|
139 |
-
names, trademarks, service marks, or product names of the Licensor,
|
140 |
-
except as required for reasonable and customary use in describing the
|
141 |
-
origin of the Work and reproducing the content of the NOTICE file.
|
142 |
-
|
143 |
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
144 |
-
agreed to in writing, Licensor provides the Work (and each
|
145 |
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
146 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
147 |
-
implied, including, without limitation, any warranties or conditions
|
148 |
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
149 |
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
150 |
-
appropriateness of using or redistributing the Work and assume any
|
151 |
-
risks associated with Your exercise of permissions under this License.
|
152 |
-
|
153 |
-
8. Limitation of Liability. In no event and under no legal theory,
|
154 |
-
whether in tort (including negligence), contract, or otherwise,
|
155 |
-
unless required by applicable law (such as deliberate and grossly
|
156 |
-
negligent acts) or agreed to in writing, shall any Contributor be
|
157 |
-
liable to You for damages, including any direct, indirect, special,
|
158 |
-
incidental, or consequential damages of any character arising as a
|
159 |
-
result of this License or out of the use or inability to use the
|
160 |
-
Work (including but not limited to damages for loss of goodwill,
|
161 |
-
work stoppage, computer failure or malfunction, or any and all
|
162 |
-
other commercial damages or losses), even if such Contributor
|
163 |
-
has been advised of the possibility of such damages.
|
164 |
-
|
165 |
-
9. Accepting Warranty or Additional Liability. While redistributing
|
166 |
-
the Work or Derivative Works thereof, You may choose to offer,
|
167 |
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
168 |
-
or other liability obligations and/or rights consistent with this
|
169 |
-
License. However, in accepting such obligations, You may act only
|
170 |
-
on Your own behalf and on Your sole responsibility, not on behalf
|
171 |
-
of any other Contributor, and only if You agree to indemnify,
|
172 |
-
defend, and hold each Contributor harmless for any liability
|
173 |
-
incurred by, or claims asserted against, such Contributor by reason
|
174 |
-
of your accepting any such warranty or additional liability.
|
175 |
-
|
176 |
-
END OF TERMS AND CONDITIONS
|
177 |
-
|
178 |
-
APPENDIX: How to apply the Apache License to your work.
|
179 |
-
|
180 |
-
To apply the Apache License to your work, attach the following
|
181 |
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
182 |
-
replaced with your own identifying information. (Don't include
|
183 |
-
the brackets!) The text should be enclosed in the appropriate
|
184 |
-
comment syntax for the file format. We also recommend that a
|
185 |
-
file or class name and description of purpose be included on the
|
186 |
-
same "printed page" as the copyright notice for easier
|
187 |
-
identification within third-party archives.
|
188 |
-
|
189 |
-
Copyright [yyyy] [name of copyright owner]
|
190 |
-
|
191 |
-
Licensed under the Apache License, Version 2.0 (the "License");
|
192 |
-
you may not use this file except in compliance with the License.
|
193 |
-
You may obtain a copy of the License at
|
194 |
-
|
195 |
-
http://www.apache.org/licenses/LICENSE-2.0
|
196 |
-
|
197 |
-
Unless required by applicable law or agreed to in writing, software
|
198 |
-
distributed under the License is distributed on an "AS IS" BASIS,
|
199 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200 |
-
See the License for the specific language governing permissions and
|
201 |
-
limitations under the License.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
@@ -7,25 +7,34 @@ original LynxKite. The primary goals of this rewrite are:
|
|
7 |
- More extensible backend. Make it easy to add new LynxKite boxes. Make it easy to use our frontend for other purposes,
|
8 |
configuring and executing other pipelines.
|
9 |
|
10 |
-
##
|
11 |
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
```bash
|
15 |
-
|
16 |
-
|
|
|
17 |
```
|
18 |
|
19 |
-
|
20 |
|
21 |
```bash
|
22 |
-
cd
|
23 |
-
|
24 |
-
npm run dev
|
25 |
```
|
26 |
|
27 |
-
|
28 |
|
29 |
```bash
|
30 |
-
|
|
|
31 |
```
|
|
|
7 |
- More extensible backend. Make it easy to add new LynxKite boxes. Make it easy to use our frontend for other purposes,
|
8 |
configuring and executing other pipelines.
|
9 |
|
10 |
+
## Structure
|
11 |
|
12 |
+
- `lynxkite-core`: Core types and utilities. Depend on this lightweight package if you are writing LynxKite plugins.
|
13 |
+
- `lynxkite-app`: The LynxKite web application. Install some plugins then run this to use LynxKite.
|
14 |
+
- `lynxkite-graph-analytics`: Graph analytics plugin. The classical LynxKite experience!
|
15 |
+
- `lynxkite-pillow`: A simple example plugin.
|
16 |
+
- `lynxkite-lynxscribe`: A plugin for building and running LynxScribe applications.
|
17 |
+
|
18 |
+
## Development
|
19 |
+
|
20 |
+
Install everything like this:
|
21 |
|
22 |
```bash
|
23 |
+
uv venv
|
24 |
+
source .venv/bin/activate
|
25 |
+
uv pip install -e lynxkite-core/ lynxkite-app/ lynxkite-graph-analytics/ lynxkite-lynxscribe/ lynxkite-pillow-example/
|
26 |
```
|
27 |
|
28 |
+
This also builds the frontend, hopefully very quickly. To run it:
|
29 |
|
30 |
```bash
|
31 |
+
cd lynxkite-app # just because the "data" directory with the examples is here
|
32 |
+
LYNXKITE_RELOAD=1 lynxkite
|
|
|
33 |
```
|
34 |
|
35 |
+
If you also want to make changes to the frontend with hot reloading:
|
36 |
|
37 |
```bash
|
38 |
+
cd lynxkite-app/web
|
39 |
+
npm run dev
|
40 |
```
|
lynxkite-app/.gitignore
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
/src/lynxkite/app/web_assets
|
2 |
+
!/src/lynxkite/app/web_assets/__init__.py
|
3 |
+
!/src/lynxkite/app/web_assets/assets/__init__.py
|
lynxkite-app/MANIFEST.in
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
graft web
|
2 |
+
prune web/node_modules
|
lynxkite-app/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# LynxKite MM
|
2 |
+
|
3 |
+
This is an experimental rewrite of [LynxKite](https://github.com/lynxkite/lynxkite). It is not compatible with the
|
4 |
+
original LynxKite. The primary goals of this rewrite are:
|
5 |
+
|
6 |
+
- Target GPU clusters instead of Hadoop clusters. We use Python instead of Scala, RAPIDS instead of Apache Spark.
|
7 |
+
- More extensible backend. Make it easy to add new LynxKite boxes. Make it easy to use our frontend for other purposes,
|
8 |
+
configuring and executing other pipelines.
|
9 |
+
|
10 |
+
## Development
|
11 |
+
|
12 |
+
To run the backend:
|
13 |
+
|
14 |
+
```bash
|
15 |
+
PYTHONPATH=. uv run pydantic2ts --module server.workspace --output ./web/src/apiTypes.ts --json2ts-cmd "npm exec --prefix web json2ts"
|
16 |
+
uv run fastapi run server/main.py --reload
|
17 |
+
```
|
18 |
+
|
19 |
+
To run the frontend:
|
20 |
+
|
21 |
+
```bash
|
22 |
+
cd web
|
23 |
+
npm i
|
24 |
+
npm run dev
|
25 |
+
```
|
{data β lynxkite-app/data}/Image processing
RENAMED
File without changes
|
{data β lynxkite-app/data}/NetworkX demo
RENAMED
File without changes
|
{data β lynxkite-app/data}/PyTorch demo
RENAMED
File without changes
|
{data β lynxkite-app/data}/sql
RENAMED
File without changes
|
lynxkite-app/pyproject.toml
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[project]
|
2 |
+
name = "lynxkite"
|
3 |
+
version = "0.1.0"
|
4 |
+
description = "The LynxKite application, with web server and UI"
|
5 |
+
readme = "README.md"
|
6 |
+
requires-python = ">=3.11"
|
7 |
+
dependencies = [
|
8 |
+
"fastapi[standard]>=0.115.6",
|
9 |
+
"lynxkite-core",
|
10 |
+
"orjson>=3.10.13",
|
11 |
+
"pycrdt-websocket>=0.15.3",
|
12 |
+
"pydantic-to-typescript>=2.0.0",
|
13 |
+
"sse-starlette>=2.2.1",
|
14 |
+
]
|
15 |
+
|
16 |
+
[tool.uv.sources]
|
17 |
+
lynxkite-core = { path = "../lynxkite-core" }
|
18 |
+
|
19 |
+
[build-system]
|
20 |
+
requires = ["setuptools", "wheel", "setuptools-scm"]
|
21 |
+
build-backend = "setuptools.build_meta"
|
22 |
+
|
23 |
+
[tool.setuptools.packages.find]
|
24 |
+
namespaces = true
|
25 |
+
where = ["src"]
|
26 |
+
|
27 |
+
[tool.setuptools.package-data]
|
28 |
+
"lynxkite.app.web_assets" = ["*"]
|
29 |
+
"lynxkite.app.web_assets.assets" = ["*"]
|
30 |
+
|
31 |
+
[tool.setuptools]
|
32 |
+
py-modules = ["build_frontend"]
|
33 |
+
include-package-data = true
|
34 |
+
|
35 |
+
[tool.setuptools.cmdclass]
|
36 |
+
build_py = "build_frontend.build_py"
|
37 |
+
|
38 |
+
[project.scripts]
|
39 |
+
lynxkite = "lynxkite.app.__main__:main"
|
lynxkite-app/src/build_frontend.py
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Customized build process for setuptools."""
|
2 |
+
|
3 |
+
import subprocess
|
4 |
+
from setuptools.command.build_py import build_py as _build_py
|
5 |
+
from pathlib import Path
|
6 |
+
import shutil
|
7 |
+
|
8 |
+
|
9 |
+
class build_py(_build_py):
|
10 |
+
def run(self):
|
11 |
+
print("\n\nBuilding frontend...", __file__)
|
12 |
+
here = Path(__file__).parent.parent
|
13 |
+
frontend_dir = here / "web"
|
14 |
+
package_dir = here / "src" / "lynxkite" / "app" / "web_assets"
|
15 |
+
subprocess.check_call(["npm", "install"], cwd=frontend_dir)
|
16 |
+
subprocess.check_call(["npm", "run", "build"], cwd=frontend_dir)
|
17 |
+
print("files in", frontend_dir / "dist")
|
18 |
+
for file in (frontend_dir / "dist").iterdir():
|
19 |
+
print(file)
|
20 |
+
# shutil.rmtree(package_dir)
|
21 |
+
shutil.copytree(frontend_dir / "dist", package_dir, dirs_exist_ok=True)
|
22 |
+
# (frontend_dir / "dist").rename(package_dir)
|
23 |
+
print("files in", package_dir)
|
24 |
+
for file in package_dir.iterdir():
|
25 |
+
print(file)
|
26 |
+
super().run()
|
{server β lynxkite-app/src/lynxkite/app}/__init__.py
RENAMED
File without changes
|
lynxkite-app/src/lynxkite/app/__main__.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import uvicorn
|
2 |
+
from .main import app # noqa: F401
|
3 |
+
import os
|
4 |
+
|
5 |
+
|
6 |
+
def main():
|
7 |
+
port = int(os.environ.get("PORT", "8000"))
|
8 |
+
reload = bool(os.environ.get("LYNXKITE_RELOAD", ""))
|
9 |
+
uvicorn.run("lynxkite.app.main:app", host="0.0.0.0", port=port, reload=reload)
|
10 |
+
|
11 |
+
|
12 |
+
if __name__ == "__main__":
|
13 |
+
main()
|
{server β lynxkite-app/src/lynxkite/app}/crdt.py
RENAMED
@@ -11,6 +11,7 @@ import pycrdt_websocket
|
|
11 |
import pycrdt_websocket.ystore
|
12 |
import uvicorn
|
13 |
import builtins
|
|
|
14 |
|
15 |
router = fastapi.APIRouter()
|
16 |
DATA_PATH = pathlib.Path.cwd() / "data"
|
@@ -119,8 +120,6 @@ def crdt_update(crdt_obj, python_obj, boxes=set()):
|
|
119 |
|
120 |
|
121 |
def try_to_load_workspace(ws, name):
|
122 |
-
from . import workspace
|
123 |
-
|
124 |
json_path = f"data/{name}"
|
125 |
if os.path.exists(json_path):
|
126 |
ws_pyd = workspace.load(json_path)
|
@@ -132,15 +131,14 @@ delayed_executions = {}
|
|
132 |
|
133 |
|
134 |
async def workspace_changed(name, changes, ws_crdt):
|
135 |
-
from . import workspace
|
136 |
-
|
137 |
ws_pyd = workspace.Workspace.model_validate(ws_crdt.to_py())
|
138 |
# Do not trigger execution for superficial changes.
|
139 |
# This is a quick solution until we build proper caching.
|
140 |
-
|
141 |
-
|
|
|
142 |
return
|
143 |
-
last_known_versions[name] =
|
144 |
# Frontend changes that result from typing are delayed to avoid
|
145 |
# rerunning the workspace for every keystroke.
|
146 |
if name in delayed_executions:
|
@@ -157,8 +155,6 @@ async def workspace_changed(name, changes, ws_crdt):
|
|
157 |
|
158 |
|
159 |
async def execute(name, ws_crdt, ws_pyd, delay=0):
|
160 |
-
from . import workspace
|
161 |
-
|
162 |
if delay:
|
163 |
try:
|
164 |
await asyncio.sleep(delay)
|
|
|
11 |
import pycrdt_websocket.ystore
|
12 |
import uvicorn
|
13 |
import builtins
|
14 |
+
from lynxkite.core import workspace
|
15 |
|
16 |
router = fastapi.APIRouter()
|
17 |
DATA_PATH = pathlib.Path.cwd() / "data"
|
|
|
120 |
|
121 |
|
122 |
def try_to_load_workspace(ws, name):
|
|
|
|
|
123 |
json_path = f"data/{name}"
|
124 |
if os.path.exists(json_path):
|
125 |
ws_pyd = workspace.load(json_path)
|
|
|
131 |
|
132 |
|
133 |
async def workspace_changed(name, changes, ws_crdt):
|
|
|
|
|
134 |
ws_pyd = workspace.Workspace.model_validate(ws_crdt.to_py())
|
135 |
# Do not trigger execution for superficial changes.
|
136 |
# This is a quick solution until we build proper caching.
|
137 |
+
ws_simple = ws_pyd.model_copy(deep=True)
|
138 |
+
clean_input(ws_simple)
|
139 |
+
if ws_simple == last_known_versions.get(name):
|
140 |
return
|
141 |
+
last_known_versions[name] = ws_simple
|
142 |
# Frontend changes that result from typing are delayed to avoid
|
143 |
# rerunning the workspace for every keystroke.
|
144 |
if name in delayed_executions:
|
|
|
155 |
|
156 |
|
157 |
async def execute(name, ws_crdt, ws_pyd, delay=0):
|
|
|
|
|
158 |
if delay:
|
159 |
try:
|
160 |
await asyncio.sleep(delay)
|
{server β lynxkite-app/src/lynxkite/app}/main.py
RENAMED
@@ -6,17 +6,31 @@ import fastapi
|
|
6 |
import importlib
|
7 |
import pathlib
|
8 |
import pkgutil
|
|
|
|
|
|
|
|
|
9 |
from . import crdt
|
10 |
-
from . import ops
|
11 |
-
from . import workspace
|
12 |
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
print(f"Importing {name}")
|
18 |
-
name =
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
app = fastapi.FastAPI(lifespan=crdt.lifespan)
|
22 |
app.include_router(crdt.router)
|
@@ -93,12 +107,32 @@ def make_dir(req: dict):
|
|
93 |
@app.get("/api/service/{module_path:path}")
|
94 |
async def service_get(req: fastapi.Request, module_path: str):
|
95 |
"""Executors can provide extra HTTP APIs through the /api/service endpoint."""
|
96 |
-
module =
|
97 |
return await module.api_service_get(req)
|
98 |
|
99 |
|
100 |
@app.post("/api/service/{module_path:path}")
|
101 |
async def service_post(req: fastapi.Request, module_path: str):
|
102 |
"""Executors can provide extra HTTP APIs through the /api/service endpoint."""
|
103 |
-
module =
|
104 |
return await module.api_service_post(req)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
import importlib
|
7 |
import pathlib
|
8 |
import pkgutil
|
9 |
+
from fastapi.staticfiles import StaticFiles
|
10 |
+
import starlette
|
11 |
+
from lynxkite.core import ops
|
12 |
+
from lynxkite.core import workspace
|
13 |
from . import crdt
|
|
|
|
|
14 |
|
15 |
+
|
16 |
+
def detect_plugins():
|
17 |
+
try:
|
18 |
+
import lynxkite_plugins
|
19 |
+
except ImportError:
|
20 |
+
print("No modules found in lynxkite_plugins. Be sure to install some plugins.")
|
21 |
+
return {}
|
22 |
+
|
23 |
+
plugins = {}
|
24 |
+
for _, name, _ in pkgutil.iter_modules(lynxkite_plugins.__path__):
|
25 |
+
name = f"lynxkite_plugins.{name}"
|
26 |
print(f"Importing {name}")
|
27 |
+
plugins[name] = importlib.import_module(name)
|
28 |
+
if not plugins:
|
29 |
+
print("No modules found in lynxkite_plugins. Be sure to install some plugins.")
|
30 |
+
return plugins
|
31 |
+
|
32 |
+
|
33 |
+
lynxkite_plugins = detect_plugins()
|
34 |
|
35 |
app = fastapi.FastAPI(lifespan=crdt.lifespan)
|
36 |
app.include_router(crdt.router)
|
|
|
107 |
@app.get("/api/service/{module_path:path}")
|
108 |
async def service_get(req: fastapi.Request, module_path: str):
|
109 |
"""Executors can provide extra HTTP APIs through the /api/service endpoint."""
|
110 |
+
module = lynxkite_plugins[module_path.split("/")[0]]
|
111 |
return await module.api_service_get(req)
|
112 |
|
113 |
|
114 |
@app.post("/api/service/{module_path:path}")
|
115 |
async def service_post(req: fastapi.Request, module_path: str):
|
116 |
"""Executors can provide extra HTTP APIs through the /api/service endpoint."""
|
117 |
+
module = lynxkite_plugins[module_path.split("/")[0]]
|
118 |
return await module.api_service_post(req)
|
119 |
+
|
120 |
+
|
121 |
+
class SPAStaticFiles(StaticFiles):
|
122 |
+
"""Route everything to index.html. https://stackoverflow.com/a/73552966/3318517"""
|
123 |
+
|
124 |
+
async def get_response(self, path: str, scope):
|
125 |
+
try:
|
126 |
+
return await super().get_response(path, scope)
|
127 |
+
except (
|
128 |
+
fastapi.HTTPException,
|
129 |
+
starlette.exceptions.HTTPException,
|
130 |
+
) as ex:
|
131 |
+
if ex.status_code == 404:
|
132 |
+
return await super().get_response(".", scope)
|
133 |
+
else:
|
134 |
+
raise ex
|
135 |
+
|
136 |
+
|
137 |
+
static_dir = SPAStaticFiles(packages=[("lynxkite.app", "web_assets")], html=True)
|
138 |
+
app.mount("/", static_dir, name="web_assets")
|
lynxkite-app/src/lynxkite/app/web_assets/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"""The build process (uv build) puts the frontend build artifacts here."""
|
lynxkite-app/src/lynxkite/app/web_assets/assets/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"""More frontend build artifacts."""
|
lynxkite-app/uv.lock
ADDED
The diff for this file is too large to render.
See raw diff
|
|
{web β lynxkite-app/web}/.gitignore
RENAMED
File without changes
|
{web β lynxkite-app/web}/README.md
RENAMED
File without changes
|
{web β lynxkite-app/web}/eslint.config.js
RENAMED
File without changes
|
{web β lynxkite-app/web}/index.html
RENAMED
File without changes
|
{web β lynxkite-app/web}/package-lock.json
RENAMED
File without changes
|
{web β lynxkite-app/web}/package.json
RENAMED
@@ -4,10 +4,10 @@
|
|
4 |
"version": "0.0.0",
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
-
"dev": "vite",
|
8 |
-
"build": "tsc -b && vite build",
|
9 |
-
"lint": "eslint .",
|
10 |
-
"preview": "vite preview"
|
11 |
},
|
12 |
"dependencies": {
|
13 |
"@esbuild/linux-x64": "^0.24.0",
|
|
|
4 |
"version": "0.0.0",
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
+
"dev": "npx vite",
|
8 |
+
"build": "npx tsc -b && npx vite build",
|
9 |
+
"lint": "npx eslint .",
|
10 |
+
"preview": "npx vite preview"
|
11 |
},
|
12 |
"dependencies": {
|
13 |
"@esbuild/linux-x64": "^0.24.0",
|
{web β lynxkite-app/web}/postcss.config.js
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/Directory.tsx
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/apiTypes.ts
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/assets/favicon.ico
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/assets/logo.png
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/index.css
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/main.tsx
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/vite-env.d.ts
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/workspace/EnvironmentSelector.tsx
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/workspace/LynxKiteState.ts
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/workspace/NodeSearch.tsx
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/workspace/Workspace.tsx
RENAMED
@@ -57,7 +57,7 @@ function LynxKiteFlow() {
|
|
57 |
const state = syncedStore({ workspace: {} as Workspace });
|
58 |
setState(state);
|
59 |
const doc = getYjsDoc(state);
|
60 |
-
const wsProvider = new WebsocketProvider(
|
61 |
const onChange = (_update: any, origin: any, _doc: any, _tr: any) => {
|
62 |
if (origin === wsProvider) {
|
63 |
// An update from the CRDT. Apply it to the local state.
|
|
|
57 |
const state = syncedStore({ workspace: {} as Workspace });
|
58 |
setState(state);
|
59 |
const doc = getYjsDoc(state);
|
60 |
+
const wsProvider = new WebsocketProvider(`ws://${location.host}/ws/crdt`, path!, doc);
|
61 |
const onChange = (_update: any, origin: any, _doc: any, _tr: any) => {
|
62 |
if (origin === wsProvider) {
|
63 |
// An update from the CRDT. Apply it to the local state.
|
{web β lynxkite-app/web}/src/workspace/nodes/LynxKiteNode.tsx
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/workspace/nodes/NodeParameter.tsx
RENAMED
@@ -1,10 +1,17 @@
|
|
1 |
const BOOLEAN = "<class 'bool'>";
|
2 |
|
3 |
-
function ParamName({ name }) {
|
4 |
return <span className="param-name bg-base-200">{name.replace(/_/g, ' ')}</span>;
|
5 |
}
|
6 |
|
7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
return (
|
9 |
<label className="param">
|
10 |
{meta?.type?.format === 'collapsed' ? <>
|
@@ -25,7 +32,7 @@ export default function NodeParameter({ name, value, meta, onChange }) {
|
|
25 |
value={value || meta.type.enum[0]}
|
26 |
onChange={(evt) => onChange(evt.currentTarget.value)}
|
27 |
>
|
28 |
-
{meta.type.enum.map(option =>
|
29 |
<option key={option} value={option}>{option}</option>
|
30 |
)}
|
31 |
</select>
|
|
|
1 |
const BOOLEAN = "<class 'bool'>";
|
2 |
|
3 |
+
function ParamName({ name }: { name: string }) {
|
4 |
return <span className="param-name bg-base-200">{name.replace(/_/g, ' ')}</span>;
|
5 |
}
|
6 |
|
7 |
+
interface NodeParameterProps {
|
8 |
+
name: string;
|
9 |
+
value: any;
|
10 |
+
meta: any;
|
11 |
+
onChange: (value: any, options?: { delay: number }) => void;
|
12 |
+
}
|
13 |
+
|
14 |
+
export default function NodeParameter({ name, value, meta, onChange }: NodeParameterProps) {
|
15 |
return (
|
16 |
<label className="param">
|
17 |
{meta?.type?.format === 'collapsed' ? <>
|
|
|
32 |
value={value || meta.type.enum[0]}
|
33 |
onChange={(evt) => onChange(evt.currentTarget.value)}
|
34 |
>
|
35 |
+
{meta.type.enum.map((option: string) =>
|
36 |
<option key={option} value={option}>{option}</option>
|
37 |
)}
|
38 |
</select>
|
{web β lynxkite-app/web}/src/workspace/nodes/NodeWithImage.tsx
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/workspace/nodes/NodeWithParams.tsx
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/workspace/nodes/NodeWithTableView.tsx
RENAMED
File without changes
|
{web β lynxkite-app/web}/src/workspace/nodes/NodeWithVisualization.tsx
RENAMED
@@ -1,6 +1,6 @@
|
|
1 |
import React, { useEffect } from 'react';
|
2 |
import NodeWithParams from './NodeWithParams';
|
3 |
-
|
4 |
|
5 |
const NodeWithVisualization = (props: any) => {
|
6 |
const chartsRef = React.useRef<HTMLDivElement>(null);
|
|
|
1 |
import React, { useEffect } from 'react';
|
2 |
import NodeWithParams from './NodeWithParams';
|
3 |
+
const echarts = await import('echarts');
|
4 |
|
5 |
const NodeWithVisualization = (props: any) => {
|
6 |
const chartsRef = React.useRef<HTMLDivElement>(null);
|
{web β lynxkite-app/web}/src/workspace/nodes/Table.tsx
RENAMED
File without changes
|
{web β lynxkite-app/web}/tailwind.config.js
RENAMED
@@ -7,6 +7,7 @@ export default {
|
|
7 |
},
|
8 |
plugins: [require('daisyui')],
|
9 |
daisyui: {
|
|
|
10 |
themes: [
|
11 |
{
|
12 |
lynxkite: {
|
|
|
7 |
},
|
8 |
plugins: [require('daisyui')],
|
9 |
daisyui: {
|
10 |
+
logs: false,
|
11 |
themes: [
|
12 |
{
|
13 |
lynxkite: {
|
{web β lynxkite-app/web}/tsconfig.app.json
RENAMED
File without changes
|
{web β lynxkite-app/web}/tsconfig.json
RENAMED
File without changes
|
{web β lynxkite-app/web}/tsconfig.node.json
RENAMED
File without changes
|
{web β lynxkite-app/web}/vite.config.ts
RENAMED
@@ -4,6 +4,15 @@ import Icons from 'unplugin-icons/vite'
|
|
4 |
|
5 |
// https://vite.dev/config/
|
6 |
export default defineConfig({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
plugins: [
|
8 |
react(),
|
9 |
Icons({ compiler: 'jsx', jsx: 'react' }),
|
|
|
4 |
|
5 |
// https://vite.dev/config/
|
6 |
export default defineConfig({
|
7 |
+
build: {
|
8 |
+
chunkSizeWarningLimit: 2048,
|
9 |
+
},
|
10 |
+
esbuild: {
|
11 |
+
supported: {
|
12 |
+
// For dynamic imports.
|
13 |
+
'top-level-await': true,
|
14 |
+
},
|
15 |
+
},
|
16 |
plugins: [
|
17 |
react(),
|
18 |
Icons({ compiler: 'jsx', jsx: 'react' }),
|
lynxkite-core/README.md
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# LynxKite Core
|
2 |
+
|
3 |
+
This is a lightweight Python package for defining LynxKite operations and executors.
|
4 |
+
If you want to write LynxKite operations or executors, this is what you need!
|