Spaces:
Sleeping
Sleeping
Merge pull request #225 from biggraph/darabos-delete-lynxkite
Browse filesInstead of forking the open-source LynxKite, take it as a dependency
This view is limited to 50 files because it contains too many changes. See raw diff
- .github/workflows/docs.yaml +0 -28
- .github/workflows/test.yaml +4 -35
- .pre-commit-config.yaml +14 -24
- docs/assets/lynxkite-icon-white.png +0 -0
- docs/contributing.md +0 -56
- docs/guides/analytics.md +0 -82
- docs/guides/plugins.md +0 -283
- docs/guides/quickstart.md +0 -27
- docs/index.md +0 -5
- docs/license.md +0 -11
- docs/lynxkite-core.md +0 -6
- docs/lynxkite-graph-analytics.md +0 -6
- docs/reference/lynxkite-core/executors/one_by_one.md +0 -1
- docs/reference/lynxkite-core/executors/simple.md +0 -1
- docs/reference/lynxkite-core/ops.md +0 -1
- docs/reference/lynxkite-core/workspace.md +0 -1
- docs/reference/lynxkite-graph-analytics/core.md +0 -1
- docs/reference/lynxkite-graph-analytics/operations.md +0 -3
- docs/stylesheets/extra.css +0 -8
- examples/Airlines demo.lynxkite.json +0 -0
- examples/Cheminformatics/chembl_tools.py +1 -1
- examples/Cheminformatics/cheminfo_tools.py +1 -1
- examples/Cheminformatics/rcsb_api.py +1 -1
- examples/Image processing.lynxkite.json +0 -303
- examples/Image table.lynxkite.json +0 -364
- examples/Model definition.lynxkite.json +0 -671
- examples/Model use.lynxkite.json +0 -0
- examples/Multi-output demo.lynxkite.json +0 -301
- examples/NetworkX demo.lynxkite.json +0 -0
- examples/Word2vec.lynxkite.json +0 -0
- examples/fake_data.py +0 -21
- examples/make_image_table.py +0 -11
- examples/matplotlib_example.py +0 -34
- examples/multi_output_demo.py +0 -24
- examples/ode_lstm.py +0 -54
- examples/requirements.txt +0 -3
- examples/sql.lynxkite.json +0 -0
- examples/uploads/example-pizza.md +0 -136
- examples/uploads/molecules2.csv +0 -4
- examples/uploads/plus-one-dataset.parquet +0 -0
- examples/word2vec.py +0 -27
- lynxkite-app/.gitignore +0 -5
- lynxkite-app/MANIFEST.in +0 -2
- lynxkite-app/README.md +0 -31
- lynxkite-app/pyproject.toml +0 -60
- lynxkite-app/src/build_frontend.py +0 -26
- lynxkite-app/src/lynxkite_app/__init__.py +0 -0
- lynxkite-app/src/lynxkite_app/__main__.py +0 -20
- lynxkite-app/src/lynxkite_app/crdt.py +0 -342
- lynxkite-app/src/lynxkite_app/main.py +0 -165
.github/workflows/docs.yaml
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
name: docs
|
| 2 |
-
|
| 3 |
-
on:
|
| 4 |
-
push:
|
| 5 |
-
branches: [main]
|
| 6 |
-
permissions:
|
| 7 |
-
contents: write
|
| 8 |
-
jobs:
|
| 9 |
-
deploy:
|
| 10 |
-
runs-on: ubuntu-latest
|
| 11 |
-
steps:
|
| 12 |
-
- uses: actions/checkout@v4
|
| 13 |
-
- name: Configure Git Credentials
|
| 14 |
-
run: |
|
| 15 |
-
git config user.name github-actions[bot]
|
| 16 |
-
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
| 17 |
-
- uses: actions/setup-python@v5
|
| 18 |
-
with:
|
| 19 |
-
python-version: 3.x
|
| 20 |
-
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
| 21 |
-
- uses: actions/cache@v4
|
| 22 |
-
with:
|
| 23 |
-
key: mkdocs-material-${{ env.cache_id }}
|
| 24 |
-
path: .cache
|
| 25 |
-
restore-keys: |
|
| 26 |
-
mkdocs-material-
|
| 27 |
-
- run: pip install mkdocs-material mkdocstrings-python mkdocs-autorefs
|
| 28 |
-
- run: mkdocs gh-deploy --force
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.github/workflows/test.yaml
CHANGED
|
@@ -15,53 +15,22 @@ jobs:
|
|
| 15 |
- name: Set up Python
|
| 16 |
run: uv python install
|
| 17 |
|
| 18 |
-
# - name: Debug ty issue
|
| 19 |
-
# run: |
|
| 20 |
-
# uv pip install ty
|
| 21 |
-
# uv pip list
|
| 22 |
-
# uv run python -m ty check
|
| 23 |
-
|
| 24 |
- name: Install dependencies
|
| 25 |
run: |
|
| 26 |
eval `ssh-agent -s`
|
| 27 |
ssh-add - <<< '${{ secrets.LYNXSCRIBE_DEPLOY_KEY }}'
|
| 28 |
uv venv
|
| 29 |
. .venv/bin/activate
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
-e lynxkite-app/[dev] \
|
| 33 |
-
-e lynxkite-graph-analytics/[dev] \
|
| 34 |
-
-e lynxkite-bio \
|
| 35 |
-
-e lynxkite-lynxscribe/ \
|
| 36 |
-
-e lynxkite-pillow-example/
|
| 37 |
|
| 38 |
- name: Run pre-commits
|
| 39 |
run: |
|
| 40 |
-
|
| 41 |
-
uv run pre-commit run --all-files
|
| 42 |
|
| 43 |
- name: Run Python unittests
|
| 44 |
run: |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
- name: Build the documentation
|
| 48 |
-
run: |
|
| 49 |
-
uv pip install mkdocs-material mkdocstrings[python]
|
| 50 |
-
uv run mkdocs build
|
| 51 |
-
|
| 52 |
-
- uses: actions/setup-node@v4
|
| 53 |
-
with:
|
| 54 |
-
node-version: lts/*
|
| 55 |
-
|
| 56 |
-
- name: Install frontend dependencies
|
| 57 |
-
run: |
|
| 58 |
-
cd lynxkite-app/web
|
| 59 |
-
npm i
|
| 60 |
-
npx playwright install --with-deps
|
| 61 |
-
|
| 62 |
-
- name: Run Playwright tests
|
| 63 |
-
run: |
|
| 64 |
-
uv run bash -c 'cd lynxkite-app/web; npm run build; npm run test'
|
| 65 |
|
| 66 |
- uses: actions/upload-artifact@v4
|
| 67 |
name: Upload playwright report
|
|
|
|
| 15 |
- name: Set up Python
|
| 16 |
run: uv python install
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
- name: Install dependencies
|
| 19 |
run: |
|
| 20 |
eval `ssh-agent -s`
|
| 21 |
ssh-add - <<< '${{ secrets.LYNXSCRIBE_DEPLOY_KEY }}'
|
| 22 |
uv venv
|
| 23 |
. .venv/bin/activate
|
| 24 |
+
echo PATH=$PATH >> $GITHUB_ENV
|
| 25 |
+
uv sync
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
- name: Run pre-commits
|
| 28 |
run: |
|
| 29 |
+
pre-commit run --all-files
|
|
|
|
| 30 |
|
| 31 |
- name: Run Python unittests
|
| 32 |
run: |
|
| 33 |
+
pytest
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
- uses: actions/upload-artifact@v4
|
| 36 |
name: Upload playwright report
|
.pre-commit-config.yaml
CHANGED
|
@@ -11,32 +11,22 @@ repos:
|
|
| 11 |
- id: ruff
|
| 12 |
args: [ --fix ]
|
| 13 |
- id: ruff-format
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
#
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
entry: uv run ty check
|
| 25 |
-
pass_filenames: false
|
| 26 |
-
args: [--python=.venv/]
|
| 27 |
-
additional_dependencies: [ty]
|
| 28 |
- repo: https://github.com/fpgmaas/deptry.git
|
| 29 |
rev: "0.23.0"
|
| 30 |
hooks:
|
| 31 |
- id: deptry
|
| 32 |
-
name: deptry for lynxkite-
|
| 33 |
-
entry: bash -c 'cd lynxkite-
|
| 34 |
-
- id: deptry
|
| 35 |
-
name: deptry for lynxkite-core
|
| 36 |
-
entry: bash -c 'cd lynxkite-core && deptry .'
|
| 37 |
-
- id: deptry
|
| 38 |
-
name: deptry for lynxkite-graph-analytics
|
| 39 |
-
entry: bash -c 'cd lynxkite-graph-analytics && deptry .'
|
| 40 |
- id: deptry
|
| 41 |
-
name: deptry for lynxkite-
|
| 42 |
-
entry: bash -c 'cd lynxkite-
|
|
|
|
| 11 |
- id: ruff
|
| 12 |
args: [ --fix ]
|
| 13 |
- id: ruff-format
|
| 14 |
+
# # https://github.com/astral-sh/ty/issues/269
|
| 15 |
+
# - repo: local
|
| 16 |
+
# hooks:
|
| 17 |
+
# - id: ty-check
|
| 18 |
+
# name: ty-check
|
| 19 |
+
# language: python
|
| 20 |
+
# entry: uv run ty check
|
| 21 |
+
# pass_filenames: false
|
| 22 |
+
# args: [--python=.venv/]
|
| 23 |
+
# additional_dependencies: [ty]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
- repo: https://github.com/fpgmaas/deptry.git
|
| 25 |
rev: "0.23.0"
|
| 26 |
hooks:
|
| 27 |
- id: deptry
|
| 28 |
+
name: deptry for lynxkite-bio
|
| 29 |
+
entry: bash -c 'cd lynxkite-bio && deptry .'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
- id: deptry
|
| 31 |
+
name: deptry for lynxkite-lynxscribe
|
| 32 |
+
entry: bash -c 'cd lynxkite-lynxscribe && deptry .'
|
docs/assets/lynxkite-icon-white.png
DELETED
|
Binary file (3.19 kB)
|
|
|
docs/contributing.md
DELETED
|
@@ -1,56 +0,0 @@
|
|
| 1 |
-
# Contributing
|
| 2 |
-
|
| 3 |
-
The LynxKite 2000:MM repository lives at
|
| 4 |
-
[https://github.com/lynxkite/lynxkite-2000](https://github.com/lynxkite/lynxkite-2000). Bug reports, feature requests,
|
| 5 |
-
and pull requests are welcome!
|
| 6 |
-
|
| 7 |
-
## Project structure
|
| 8 |
-
|
| 9 |
-
- `lynxkite-core`: Core types and utilities. Depend on this lightweight package if you are writing LynxKite plugins.
|
| 10 |
-
- `lynxkite-app`: The LynxKite web application. Install some plugins then run this to use LynxKite.
|
| 11 |
-
- `lynxkite-graph-analytics`: Graph analytics plugin. The classical LynxKite experience!
|
| 12 |
-
- `lynxkite-pillow`: A simple example plugin.
|
| 13 |
-
- `lynxkite-lynxscribe`: A plugin for building and running LynxScribe applications.
|
| 14 |
-
- `lynxkite-bio`: Bioinformatics additions for LynxKite Graph Analytics.
|
| 15 |
-
- `docs`: User-facing documentation. It's shared between all packages.
|
| 16 |
-
|
| 17 |
-
## Development setup
|
| 18 |
-
|
| 19 |
-
Install everything like this:
|
| 20 |
-
|
| 21 |
-
```bash
|
| 22 |
-
uv venv
|
| 23 |
-
source .venv/bin/activate
|
| 24 |
-
uvx pre-commit install
|
| 25 |
-
uv sync
|
| 26 |
-
```
|
| 27 |
-
|
| 28 |
-
This also builds the frontend, hopefully very quickly. To run it:
|
| 29 |
-
|
| 30 |
-
```bash
|
| 31 |
-
cd examples
|
| 32 |
-
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 |
-
```
|
| 41 |
-
|
| 42 |
-
## Executing tests
|
| 43 |
-
|
| 44 |
-
```bash
|
| 45 |
-
pytest # Runs all backend unit tests.
|
| 46 |
-
pytest lynxkite-core # Runs tests for one package.
|
| 47 |
-
cd lynxkite-app/web && npm run test # Runs frontend tests.
|
| 48 |
-
```
|
| 49 |
-
|
| 50 |
-
## Documentation
|
| 51 |
-
|
| 52 |
-
To work on the documentation:
|
| 53 |
-
|
| 54 |
-
```bash
|
| 55 |
-
mkdocs serve
|
| 56 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/guides/analytics.md
DELETED
|
@@ -1,82 +0,0 @@
|
|
| 1 |
-
# Graph analytics & data science
|
| 2 |
-
|
| 3 |
-
Install LynxKite with the graph analytics package:
|
| 4 |
-
|
| 5 |
-
```bash
|
| 6 |
-
pip install lynxkite lynxkite-graph-analytics
|
| 7 |
-
```
|
| 8 |
-
|
| 9 |
-
Run LynxKite in your data directory:
|
| 10 |
-
|
| 11 |
-
```bash
|
| 12 |
-
cd lynxkite-data
|
| 13 |
-
lynxkite
|
| 14 |
-
```
|
| 15 |
-
|
| 16 |
-
LynxKite by default runs on port 8000, so you can access it in your browser at
|
| 17 |
-
[http://localhost:8000](http://localhost:8000).
|
| 18 |
-
To run it on a different port, set the `PORT` environment variable (e.g., `PORT=8080 lynxkite`).
|
| 19 |
-
|
| 20 |
-
## Using a CUDA GPU
|
| 21 |
-
|
| 22 |
-
To make full use of your GPU, install the `lynxkite-graph-analytics` package with GPU support.
|
| 23 |
-
|
| 24 |
-
```bash
|
| 25 |
-
pip install lynxkite 'lynxkite-graph-analytics[gpu]'
|
| 26 |
-
```
|
| 27 |
-
|
| 28 |
-
And start it with the cuGraph backend for NetworkX:
|
| 29 |
-
|
| 30 |
-
```bash
|
| 31 |
-
NX_CUGRAPH_AUTOCONFIG=true lynxkite
|
| 32 |
-
```
|
| 33 |
-
|
| 34 |
-
## Directory browser
|
| 35 |
-
|
| 36 |
-
When you open the LynxKite web interface, you arrive in the directory browser. You see
|
| 37 |
-
the files and directories in your data directory — if you just created it, it will be empty.
|
| 38 |
-
|
| 39 |
-
You can create workspaces, [code files](plugins.md), and folders in the directory browser.
|
| 40 |
-
|
| 41 |
-
## Workspaces
|
| 42 |
-
|
| 43 |
-
A LynxKite workspace is the place where you build a data science pipeline.
|
| 44 |
-
Pipelines are built from boxes, which have inputs and outputs that can be connected to each other.
|
| 45 |
-
|
| 46 |
-
To place a box, click anywhere in the workspace. This opens a search menu where you can
|
| 47 |
-
find the box you want to add.
|
| 48 |
-
|
| 49 |
-
## Importing your data
|
| 50 |
-
|
| 51 |
-
To import CSV, Parquet, JSON, and Excel files, you can simply drag and drop them into the LynxKite workspace.
|
| 52 |
-
This will upload the file to the server and add an "Import file" box to the workspace.
|
| 53 |
-
|
| 54 |
-
You can also create "Import file" boxes manually and type the path to the file.
|
| 55 |
-
You can either use an absolute path, or a relative path from the data directory.
|
| 56 |
-
(Where you started LynxKite.)
|
| 57 |
-
|
| 58 |
-
## Neural network design
|
| 59 |
-
|
| 60 |
-
The graph analytics package includes two environments, _"LynxKite Graph Analytics"_, and _"PyTorch model"_.
|
| 61 |
-
Use the dropdown in the top right corner to switch to the "PyTorch model" environment.
|
| 62 |
-
This environment allows you to define neural network architectures visually.
|
| 63 |
-
|
| 64 |
-
The important parts of a neural network definition are:
|
| 65 |
-
|
| 66 |
-
- **Inputs**: These boxes stand for the inputs. You will connect them to actual data in the workspace that
|
| 67 |
-
uses this model.
|
| 68 |
-
- **Layers**: The heart of the model. Use the _"Repeat"_ box looping back from the output of a layer to the
|
| 69 |
-
input of an earlier layer to repeat a set of layers.
|
| 70 |
-
- **Outputs**: These boxes mark the place in the data flow that holds the predictions of the model.
|
| 71 |
-
- **Loss**: Build the loss computation after the output box. This part is only used during training.
|
| 72 |
-
- **Optimizer**: The result of the loss computation goes into the optimizer. Training is partially configured
|
| 73 |
-
in the optimizer box, partially in the training box in the workspace that uses the model.
|
| 74 |
-
|
| 75 |
-
Once the model is defined, you can use it in other workspaces.
|
| 76 |
-
|
| 77 |
-
- Load it with the _"Define model"_ box.
|
| 78 |
-
- Train it with the _"Train model"_ box.
|
| 79 |
-
- Generate predictions with the _"Model inference"_ box.
|
| 80 |
-
|
| 81 |
-
See the [_Model definition_ and _Model use_ workspaces](https://github.com/lynxkite/lynxkite-2000/tree/main/examples)
|
| 82 |
-
for a practical example.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/guides/plugins.md
DELETED
|
@@ -1,283 +0,0 @@
|
|
| 1 |
-
# Plugin development
|
| 2 |
-
|
| 3 |
-
Plugins can provide additional operations for an existing LynxKite environment,
|
| 4 |
-
and they can also provide new environments.
|
| 5 |
-
|
| 6 |
-
## Creating a new plugin
|
| 7 |
-
|
| 8 |
-
`.py` files inside the LynxKite data directory are automatically imported each time a
|
| 9 |
-
workspace is executed. You can create a new plugin by creating a new `.py` file in the
|
| 10 |
-
data directory. LynxKite even includes an integrated editor for this purpose.
|
| 11 |
-
Click **New code file** in the directory where you want to create the file.
|
| 12 |
-
|
| 13 |
-
Plugins in subdirectories of the data directory are imported when executing workspaces
|
| 14 |
-
within those directories. This allows you to create plugins that are only available
|
| 15 |
-
in specific workspaces.
|
| 16 |
-
|
| 17 |
-
You can also create and distribute plugins as Python packages. In this case the
|
| 18 |
-
module name must start with `lynxkite_` for it to be automatically imported on startup.
|
| 19 |
-
|
| 20 |
-
### Plugin dependencies
|
| 21 |
-
|
| 22 |
-
When creating a plugin as a "code file", you can create a `requirements.txt` file in the same
|
| 23 |
-
directory. This file will be used to install the dependencies of the plugin.
|
| 24 |
-
|
| 25 |
-
## Adding new operations
|
| 26 |
-
|
| 27 |
-
Any piece of Python code can easily be wrapped into a LynxKite operation.
|
| 28 |
-
Let's say we have some code that calculates the length of a string column in a Pandas DataFrame:
|
| 29 |
-
|
| 30 |
-
```python
|
| 31 |
-
df["length"] = df["my_column"].str.len()
|
| 32 |
-
```
|
| 33 |
-
|
| 34 |
-
We can turn it into a LynxKite operation using the
|
| 35 |
-
[`@op`](../reference/lynxkite-core/ops.md#lynxkite.core.ops.op) decorator:
|
| 36 |
-
|
| 37 |
-
```python
|
| 38 |
-
import pandas as pd
|
| 39 |
-
from lynxkite.core.ops import op
|
| 40 |
-
|
| 41 |
-
@op("LynxKite Graph Analytics", "Get column length")
|
| 42 |
-
def get_length(df: pd.DataFrame, *, column_name: str):
|
| 43 |
-
"""
|
| 44 |
-
Gets the length of a string column.
|
| 45 |
-
|
| 46 |
-
Args:
|
| 47 |
-
column_name: The name of the column to get the length of.
|
| 48 |
-
"""
|
| 49 |
-
df = df.copy()
|
| 50 |
-
df["length"] = df[column_name].str.len()
|
| 51 |
-
return df
|
| 52 |
-
```
|
| 53 |
-
|
| 54 |
-
Let's review the changes we made.
|
| 55 |
-
|
| 56 |
-
### The `@op` decorator
|
| 57 |
-
|
| 58 |
-
The [`@op`](../reference/lynxkite-core/ops.md#lynxkite.core.ops.op) decorator registers a
|
| 59 |
-
function as a LynxKite operation. The first argument is the name of the environment,
|
| 60 |
-
the last argument is the name of the operation. Between the two, you can list the hierarchy of
|
| 61 |
-
categories the operation belongs to. For example:
|
| 62 |
-
|
| 63 |
-
```python
|
| 64 |
-
@op("LynxKite Graph Analytics", "Machine learning", "Preprocessing", "Split train/test set")
|
| 65 |
-
```
|
| 66 |
-
|
| 67 |
-
When defining multiple operations, you can use
|
| 68 |
-
[`ops.op_registration`](../reference/lynxkite-core/ops.md#lynxkite.core.ops.op_registration)
|
| 69 |
-
for convenience:
|
| 70 |
-
```python
|
| 71 |
-
op = ops.op_registration("LynxKite Graph Analytics")
|
| 72 |
-
|
| 73 |
-
@op("An operation")
|
| 74 |
-
def my_op():
|
| 75 |
-
...
|
| 76 |
-
```
|
| 77 |
-
|
| 78 |
-
### The function signature
|
| 79 |
-
|
| 80 |
-
`*` in the list of function arguments marks the start of keyword-only arguments.
|
| 81 |
-
The arguments before `*` will become _inputs_ of the operation. The arguments after `*` will
|
| 82 |
-
be its _parameters_.
|
| 83 |
-
|
| 84 |
-
```python
|
| 85 |
-
# /--- inputs ---\ /- parameters -\
|
| 86 |
-
def get_length(df: pd.DataFrame, *, column_name: str):
|
| 87 |
-
```
|
| 88 |
-
|
| 89 |
-
LynxKite uses the type annotations of the function arguments to provide input validation,
|
| 90 |
-
conversion, and the right UI on the frontend.
|
| 91 |
-
|
| 92 |
-
The types supported for **inputs** are determined by the environment. For graph analytics,
|
| 93 |
-
the possibilities are:
|
| 94 |
-
|
| 95 |
-
- `pandas.DataFrame`
|
| 96 |
-
- `networkx.Graph`
|
| 97 |
-
- [`lynxkite_graph_analytics.Bundle`](../reference/lynxkite-graph-analytics/core.md#lynxkite_graph_analytics.core.Bundle)
|
| 98 |
-
|
| 99 |
-
The inputs of an operation are automatically converted to the right type, when possible.
|
| 100 |
-
|
| 101 |
-
To make an input optional, use an optional type, like `pd.DataFrame | None`.
|
| 102 |
-
|
| 103 |
-
The position of the input and output connectors can be controlled using the
|
| 104 |
-
[`@ops.input_position`](../reference/lynxkite-core/ops.md#lynxkite.core.ops.input_position) and
|
| 105 |
-
[`@ops.output_position`](../reference/lynxkite-core/ops.md#lynxkite.core.ops.output_position)
|
| 106 |
-
decorators. By default, inputs are on the left and outputs on the right.
|
| 107 |
-
|
| 108 |
-
All **parameters** are stored in LynxKite workspaces as strings. If a type annotation is provided,
|
| 109 |
-
LynxKite will convert the string to the right type and provide the right UI.
|
| 110 |
-
|
| 111 |
-
- `str`, `int`, `float` are presented as a text box and converted to the given type.
|
| 112 |
-
- `bool` is presented as a checkbox.
|
| 113 |
-
- [`lynxkite.core.ops.LongStr`](../reference/lynxkite-core/ops.md#lynxkite.core.ops.LongStr)
|
| 114 |
-
is presented as a text area.
|
| 115 |
-
- Enums are presented as a dropdown list.
|
| 116 |
-
- Pydantic models are presented as their JSON string representations. (Unless you add custom UI
|
| 117 |
-
for them.) They are converted to the model object when your function is called.
|
| 118 |
-
|
| 119 |
-
### Slow operations
|
| 120 |
-
|
| 121 |
-
If the function takes a significant amount of time to run, we must either:
|
| 122 |
-
|
| 123 |
-
- Write an asynchronous function.
|
| 124 |
-
- Pass `slow=True` to the `@op` decorator. LynxKite will run the function in a separate thread.
|
| 125 |
-
|
| 126 |
-
`slow=True` also causes the results of the operation to be cached on disk. As long as
|
| 127 |
-
its inputs don't change, the operation will not be run again. This is useful for both
|
| 128 |
-
synchronous and synchronous operations.
|
| 129 |
-
|
| 130 |
-
### Documentation
|
| 131 |
-
|
| 132 |
-
The docstring of the function is used as the operation's description. You can use
|
| 133 |
-
Google-style or Numpy-style docstrings.
|
| 134 |
-
(See [Griffe's documentation](https://mkdocstrings.github.io/griffe/reference/docstrings/).)
|
| 135 |
-
|
| 136 |
-
The docstring should be omitted for simple operations like the one above.
|
| 137 |
-
|
| 138 |
-
### Outputting results
|
| 139 |
-
|
| 140 |
-
The return value of the function is the output of the operation. It will be passed to the
|
| 141 |
-
next operation in the pipeline.
|
| 142 |
-
|
| 143 |
-
An operation can have multiple outputs. In this case, the return value must be a dictionary,
|
| 144 |
-
and the list of outputs must be declared in the `@op` decorator.
|
| 145 |
-
|
| 146 |
-
```python
|
| 147 |
-
@op("LynxKite Graph Analytics", "Train/test split", outputs=["train", "test"])
|
| 148 |
-
def test_split(df: pd.DataFrame, *, test_ratio=0.1):
|
| 149 |
-
test = df.sample(frac=test_ratio).reset_index()
|
| 150 |
-
train = df.drop(test.index).reset_index()
|
| 151 |
-
return {"train": train, "test": test}
|
| 152 |
-
```
|
| 153 |
-
|
| 154 |
-
### Displaying results
|
| 155 |
-
|
| 156 |
-
The outputs of the operation can be used by other operations. But we can also generate results
|
| 157 |
-
that are meant to be viewed by the user. The different options for this are controlled by the `view`
|
| 158 |
-
argument of the `@op` decorator.
|
| 159 |
-
|
| 160 |
-
The `view` argument can be one of the following:
|
| 161 |
-
|
| 162 |
-
- `matplotlib`: Just plot something with Matplotlib and it will be displayed in the UI.
|
| 163 |
-
|
| 164 |
-
```python
|
| 165 |
-
@op("LynxKite Graph Analytics", "Plot column histogram", view="matplotlib")
|
| 166 |
-
def plot(df: pd.DataFrame, *, column_name: str):
|
| 167 |
-
df[column_name].value_counts().sort_index().plot.bar()
|
| 168 |
-
```
|
| 169 |
-
|
| 170 |
-
- `visualization`: Draws a chart using [ECharts](https://echarts.apache.org/examples/en/index.html).
|
| 171 |
-
You need to return a dictionary with the chart configuration, which ECharts calls `option`.
|
| 172 |
-
|
| 173 |
-
```python
|
| 174 |
-
@op("View loss", view="visualization")
|
| 175 |
-
def view_loss(bundle: core.Bundle):
|
| 176 |
-
loss = bundle.dfs["training"].training_loss.tolist()
|
| 177 |
-
v = {
|
| 178 |
-
"title": {"text": "Training loss"},
|
| 179 |
-
"xAxis": {"type": "category"},
|
| 180 |
-
"yAxis": {"type": "value"},
|
| 181 |
-
"series": [{"data": loss, "type": "line"}],
|
| 182 |
-
}
|
| 183 |
-
return v
|
| 184 |
-
```
|
| 185 |
-
|
| 186 |
-
- `image`: Return an image as a
|
| 187 |
-
[data URL](https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data)
|
| 188 |
-
and it will be displayed.
|
| 189 |
-
- `molecule`: Return a molecule as a PDB or SDF string, or an `rdkit.Chem.Mol` object.
|
| 190 |
-
It will be displayed using [3Dmol.js](https://3Dmol.org/).
|
| 191 |
-
- `table_view`: Return
|
| 192 |
-
[`Bundle.to_dict()`](../reference/lynxkite-graph-analytics/core.md#lynxkite_graph_analytics.core.Bundle.to_dict).
|
| 193 |
-
|
| 194 |
-
## Adding new environments
|
| 195 |
-
|
| 196 |
-
A new environment means a completely new set of operations, and (optionally) a new
|
| 197 |
-
executor. There's nothing to be done for setting up a new environment. Just start
|
| 198 |
-
registering operations into it.
|
| 199 |
-
|
| 200 |
-
### No executor
|
| 201 |
-
|
| 202 |
-
By default, the new environment will have no executor. This can be useful!
|
| 203 |
-
|
| 204 |
-
LynxKite workspaces are stored as straightforward JSON files and updated on every modification.
|
| 205 |
-
You can use LynxKite for configuring workflows and have a separate system
|
| 206 |
-
read the JSON files.
|
| 207 |
-
|
| 208 |
-
Since the code of the operations is not executed in this case, you can create functions that do nothing.
|
| 209 |
-
Alternatively, you can use the
|
| 210 |
-
[`register_passive_op`](../reference/lynxkite-core/ops.md#lynxkite.core.ops.register_passive_op)
|
| 211 |
-
and
|
| 212 |
-
[`passive_op_registration`](../reference/lynxkite-core/ops.md#lynxkite.core.ops.passive_op_registration)
|
| 213 |
-
functions to easily whip up a set of operations:
|
| 214 |
-
|
| 215 |
-
```python
|
| 216 |
-
from lynxkite.core.ops import passive_op_registration, Parameter as P
|
| 217 |
-
|
| 218 |
-
op = passive_op_registration("My Environment")
|
| 219 |
-
op('Scrape documents', params=[P('url', '')])
|
| 220 |
-
op('Conversation logs')
|
| 221 |
-
op('Extract graph')
|
| 222 |
-
op('Compute embeddings', params=[P.options('method', ['LLM', 'graph', 'random']), P('dimensions', 1234)])
|
| 223 |
-
op('Vector DB', params=[P.options('backend', ['ANN', 'HNSW'])])
|
| 224 |
-
op('Chat UI', outputs=[])
|
| 225 |
-
op('Chat backend')
|
| 226 |
-
```
|
| 227 |
-
|
| 228 |
-
### Built-in executors
|
| 229 |
-
|
| 230 |
-
LynxKite comes with two built-in executors. You can register these for your environment
|
| 231 |
-
and you're good to go.
|
| 232 |
-
|
| 233 |
-
```python
|
| 234 |
-
from lynxkite.core.executors import simple
|
| 235 |
-
simple.register("My Environment")
|
| 236 |
-
```
|
| 237 |
-
|
| 238 |
-
The [`simple` executor](../reference/lynxkite-core/executors/simple.md)
|
| 239 |
-
runs each operation once, passing the output of the preceding operation
|
| 240 |
-
as the input to the next one. No tricks. You can use any types as inputs and outputs.
|
| 241 |
-
|
| 242 |
-
```python
|
| 243 |
-
from lynxkite.core.executors import one_by_one
|
| 244 |
-
one_by_one.register("My Environment")
|
| 245 |
-
```
|
| 246 |
-
|
| 247 |
-
The [`one_by_one` executor](../reference/lynxkite-core/executors/one_by_one.md)
|
| 248 |
-
expects that the code for operations is the code for transforming
|
| 249 |
-
a single element. If an operation returns an iterable, it will be split up
|
| 250 |
-
into its elements, and the next operation is called for each element.
|
| 251 |
-
|
| 252 |
-
Sometimes you need the full contents of an input. The `one_by_one` executor
|
| 253 |
-
lets you choose between the two modes by the orientation of the input connector.
|
| 254 |
-
If the input connector is horizontal (left or right), it takes single elements.
|
| 255 |
-
If the input connector is vertical (top or bottom), it takes an iterable of all the incoming data.
|
| 256 |
-
|
| 257 |
-
A unique advantage of this setup is that horizontal inputs can have loops across
|
| 258 |
-
horizontal inputs. Just make sure that loops eventually discard all elements, so you don't
|
| 259 |
-
end up with an infinite loop.
|
| 260 |
-
|
| 261 |
-
### Custom executors
|
| 262 |
-
|
| 263 |
-
A custom executor can be registered using
|
| 264 |
-
[`@ops.register_executor`](../reference/lynxkite-core/ops.md#lynxkite.core.ops.register_executor).
|
| 265 |
-
|
| 266 |
-
```python
|
| 267 |
-
@ops.register_executor(ENV)
|
| 268 |
-
async def execute(ws: workspace.Workspace):
|
| 269 |
-
catalog = ops.CATALOGS[ws.env]
|
| 270 |
-
...
|
| 271 |
-
```
|
| 272 |
-
|
| 273 |
-
The executor must be an asynchronous function that takes a
|
| 274 |
-
[`workspace.Workspace`](../reference/lynxkite-core/workspace.md#lynxkite.core.workspace.Workspace)
|
| 275 |
-
as an argument. The return value is ignored and it's up to you how you process the workspace.
|
| 276 |
-
|
| 277 |
-
To update the frontend as the executor processes the workspace, call
|
| 278 |
-
[`WorkspaceNode.publish_started`](../reference/lynxkite-core/workspace.md#lynxkite.core.workspace.WorkspaceNode.publish_started)
|
| 279 |
-
when starting to execute a node, and
|
| 280 |
-
[`WorkspaceNode.publish_result`](../reference/lynxkite-core/workspace.md#lynxkite.core.workspace.WorkspaceNode.publish_result)
|
| 281 |
-
to publish the results. Use
|
| 282 |
-
[`WorkspaceNode.publish_error`](../reference/lynxkite-core/workspace.md#lynxkite.core.workspace.WorkspaceNode.publish_error)
|
| 283 |
-
if the node failed.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/guides/quickstart.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
# Quickstart
|
| 2 |
-
|
| 3 |
-
Install the LynxKite application with `pip`:
|
| 4 |
-
```bash
|
| 5 |
-
pip install lynxkite
|
| 6 |
-
```
|
| 7 |
-
|
| 8 |
-
To be able to do anything useful, you also need to install one or more LynxKite environments.
|
| 9 |
-
If you want to work with data science and graph analytics, install the `lynxkite-graph-analytics` package:
|
| 10 |
-
```bash
|
| 11 |
-
pip install lynxkite-graph-analytics
|
| 12 |
-
```
|
| 13 |
-
|
| 14 |
-
Create a folder for storing your LynxKite projects:
|
| 15 |
-
```bash
|
| 16 |
-
mkdir ~/lynxkite_projects
|
| 17 |
-
```
|
| 18 |
-
|
| 19 |
-
You're ready to run LynxKite!
|
| 20 |
-
```bash
|
| 21 |
-
cd ~/lynxkite_projects
|
| 22 |
-
lynxkite
|
| 23 |
-
```
|
| 24 |
-
|
| 25 |
-
Open [http://localhost:8000/](http://localhost:8000/) in your browser.
|
| 26 |
-
|
| 27 |
-
Find example workspaces in the [`examples` folder](https://github.com/lynxkite/lynxkite-2000/tree/main/examples).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/index.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: Overview
|
| 3 |
-
---
|
| 4 |
-
|
| 5 |
-
--8<-- "README.md"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/license.md
DELETED
|
@@ -1,11 +0,0 @@
|
|
| 1 |
-
# License
|
| 2 |
-
|
| 3 |
-
LynxKite 2000:MM is available under the GNU AGPLv3 license below.
|
| 4 |
-
|
| 5 |
-
Additionally, [Lynx Analytics](https://www.lynxanalytics.com/) offers a commercial license of LynxKite 2000:MM
|
| 6 |
-
that includes additional features and support. Get in touch if you are interested in life sciences tools
|
| 7 |
-
and cluster deployment!
|
| 8 |
-
|
| 9 |
-
```
|
| 10 |
-
--8<-- "LICENSE"
|
| 11 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/lynxkite-core.md
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
# LynxKite Core
|
| 2 |
-
|
| 3 |
-
LynxKite core is for writing LynxKite plugins.
|
| 4 |
-
It contains core types and utilities that can be used by all LynxKite plugins.
|
| 5 |
-
|
| 6 |
-
::: lynxkite.core.ops
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/lynxkite-graph-analytics.md
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
# LynxKite Graph Analytics
|
| 2 |
-
|
| 3 |
-
This is the classical LynxKite experience!
|
| 4 |
-
The graph analytics plugin is a collection of graph algorithms that can be run on a LynxKite graph.
|
| 5 |
-
|
| 6 |
-
::: lynxkite_graph_analytics.lynxkite_ops
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/reference/lynxkite-core/executors/one_by_one.md
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
::: lynxkite.core.executors.one_by_one
|
|
|
|
|
|
docs/reference/lynxkite-core/executors/simple.md
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
::: lynxkite.core.executors.simple
|
|
|
|
|
|
docs/reference/lynxkite-core/ops.md
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
::: lynxkite.core.ops
|
|
|
|
|
|
docs/reference/lynxkite-core/workspace.md
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
::: lynxkite.core.workspace
|
|
|
|
|
|
docs/reference/lynxkite-graph-analytics/core.md
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
::: lynxkite_graph_analytics.core
|
|
|
|
|
|
docs/reference/lynxkite-graph-analytics/operations.md
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
::: lynxkite_graph_analytics.lynxkite_ops
|
| 2 |
-
::: lynxkite_graph_analytics.ml_ops
|
| 3 |
-
::: lynxkite_graph_analytics.networkx_ops
|
|
|
|
|
|
|
|
|
|
|
|
docs/stylesheets/extra.css
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
[data-md-color-scheme="lynxkite"] {
|
| 2 |
-
--md-primary-fg-color: #39bcf9;
|
| 3 |
-
--md-primary-fg-color--light: #9fdef9;
|
| 4 |
-
--md-primary-fg-color--dark: #096f9a;
|
| 5 |
-
--md-accent-fg-color: #ff8800;
|
| 6 |
-
--md-accent-fg-color--dark: #b86200;
|
| 7 |
-
--md-accent-fg-color--light: #ffbc70;
|
| 8 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/Airlines demo.lynxkite.json
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
examples/Cheminformatics/chembl_tools.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from
|
| 2 |
import pandas as pd
|
| 3 |
from chembl_webresource_client.new_client import new_client
|
| 4 |
from rdkit import Chem
|
|
|
|
| 1 |
+
from lynxkite_core.ops import op
|
| 2 |
import pandas as pd
|
| 3 |
from chembl_webresource_client.new_client import new_client
|
| 4 |
from rdkit import Chem
|
examples/Cheminformatics/cheminfo_tools.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import os
|
| 2 |
import pickle
|
| 3 |
-
from
|
| 4 |
from matplotlib import pyplot as plt
|
| 5 |
import pandas as pd
|
| 6 |
from rdkit.Chem.Draw import rdMolDraw2D
|
|
|
|
| 1 |
import os
|
| 2 |
import pickle
|
| 3 |
+
from lynxkite_core.ops import op
|
| 4 |
from matplotlib import pyplot as plt
|
| 5 |
import pandas as pd
|
| 6 |
from rdkit.Chem.Draw import rdMolDraw2D
|
examples/Cheminformatics/rcsb_api.py
CHANGED
|
@@ -4,7 +4,7 @@ import pypdb
|
|
| 4 |
import biotite.database.rcsb as rcsb
|
| 5 |
from MDAnalysis.analysis import rms
|
| 6 |
from opencadd.structure.superposition.engines.mda import MDAnalysisAligner
|
| 7 |
-
from
|
| 8 |
import os
|
| 9 |
import numpy as np
|
| 10 |
from Bio.PDB import PDBList, PDBParser, Superimposer
|
|
|
|
| 4 |
import biotite.database.rcsb as rcsb
|
| 5 |
from MDAnalysis.analysis import rms
|
| 6 |
from opencadd.structure.superposition.engines.mda import MDAnalysisAligner
|
| 7 |
+
from lynxkite_core.ops import op
|
| 8 |
import os
|
| 9 |
import numpy as np
|
| 10 |
from Bio.PDB import PDBList, PDBParser, Superimposer
|
examples/Image processing.lynxkite.json
DELETED
|
@@ -1,303 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"edges": [
|
| 3 |
-
{
|
| 4 |
-
"id": "xy-edge__Open image 1output-View image 1image",
|
| 5 |
-
"source": "Open image 1",
|
| 6 |
-
"sourceHandle": "output",
|
| 7 |
-
"target": "View image 1",
|
| 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 |
-
],
|
| 39 |
-
"env": "Pillow",
|
| 40 |
-
"nodes": [
|
| 41 |
-
{
|
| 42 |
-
"data": {
|
| 43 |
-
"__execution_delay": 0.0,
|
| 44 |
-
"collapsed": null,
|
| 45 |
-
"display": null,
|
| 46 |
-
"error": null,
|
| 47 |
-
"input_metadata": null,
|
| 48 |
-
"meta": {
|
| 49 |
-
"inputs": [],
|
| 50 |
-
"name": "Open image",
|
| 51 |
-
"outputs": [
|
| 52 |
-
{
|
| 53 |
-
"name": "output",
|
| 54 |
-
"position": "right",
|
| 55 |
-
"type": {
|
| 56 |
-
"type": "None"
|
| 57 |
-
}
|
| 58 |
-
}
|
| 59 |
-
],
|
| 60 |
-
"params": [
|
| 61 |
-
{
|
| 62 |
-
"default": null,
|
| 63 |
-
"name": "filename",
|
| 64 |
-
"type": {
|
| 65 |
-
"type": "<class 'str'>"
|
| 66 |
-
}
|
| 67 |
-
}
|
| 68 |
-
],
|
| 69 |
-
"type": "basic"
|
| 70 |
-
},
|
| 71 |
-
"params": {
|
| 72 |
-
"filename": "https://media.licdn.com/dms/image/v2/C4E03AQEq4tdJKQiNHQ/profile-displayphoto-shrink_200_200/profile-displayphoto-shrink_200_200/0/1657270040827?e=2147483647&v=beta&t=lDxix0_0-_K7NUFqgPdzxY5-P7f73bWpPS_XRre842c"
|
| 73 |
-
},
|
| 74 |
-
"status": "done",
|
| 75 |
-
"title": "Open image"
|
| 76 |
-
},
|
| 77 |
-
"dragHandle": ".bg-primary",
|
| 78 |
-
"height": 222.0,
|
| 79 |
-
"id": "Open image 1",
|
| 80 |
-
"parentId": null,
|
| 81 |
-
"position": {
|
| 82 |
-
"x": -316.51795927908694,
|
| 83 |
-
"y": 122.80901061526373
|
| 84 |
-
},
|
| 85 |
-
"type": "basic",
|
| 86 |
-
"width": 422.0
|
| 87 |
-
},
|
| 88 |
-
{
|
| 89 |
-
"data": {
|
| 90 |
-
"display": "data:image/jpeg;base64,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",
|
| 91 |
-
"error": null,
|
| 92 |
-
"input_metadata": null,
|
| 93 |
-
"meta": {
|
| 94 |
-
"inputs": [
|
| 95 |
-
{
|
| 96 |
-
"name": "image",
|
| 97 |
-
"position": "left",
|
| 98 |
-
"type": {
|
| 99 |
-
"type": "<module 'PIL.Image' from '/media/nvme/darabos/lynxkite-2024/.venv/lib/python3.11/site-packages/PIL/Image.py'>"
|
| 100 |
-
}
|
| 101 |
-
}
|
| 102 |
-
],
|
| 103 |
-
"name": "View image",
|
| 104 |
-
"outputs": [],
|
| 105 |
-
"params": [],
|
| 106 |
-
"type": "image"
|
| 107 |
-
},
|
| 108 |
-
"params": {},
|
| 109 |
-
"status": "done",
|
| 110 |
-
"title": "View image"
|
| 111 |
-
},
|
| 112 |
-
"dragHandle": ".bg-primary",
|
| 113 |
-
"height": 288.0,
|
| 114 |
-
"id": "View image 1",
|
| 115 |
-
"parentId": null,
|
| 116 |
-
"position": {
|
| 117 |
-
"x": 371.2152385614552,
|
| 118 |
-
"y": -243.68185336918702
|
| 119 |
-
},
|
| 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==",
|
| 126 |
-
"error": null,
|
| 127 |
-
"input_metadata": null,
|
| 128 |
-
"meta": {
|
| 129 |
-
"inputs": [
|
| 130 |
-
{
|
| 131 |
-
"name": "image",
|
| 132 |
-
"position": "left",
|
| 133 |
-
"type": {
|
| 134 |
-
"type": "<module 'PIL.Image' from '/media/nvme/darabos/lynxkite-2024/.venv/lib/python3.11/site-packages/PIL/Image.py'>"
|
| 135 |
-
}
|
| 136 |
-
}
|
| 137 |
-
],
|
| 138 |
-
"name": "View image",
|
| 139 |
-
"outputs": [],
|
| 140 |
-
"params": [],
|
| 141 |
-
"type": "image"
|
| 142 |
-
},
|
| 143 |
-
"params": {},
|
| 144 |
-
"status": "done",
|
| 145 |
-
"title": "View image"
|
| 146 |
-
},
|
| 147 |
-
"dragHandle": ".bg-primary",
|
| 148 |
-
"height": 456.0,
|
| 149 |
-
"id": "View image 2",
|
| 150 |
-
"parentId": null,
|
| 151 |
-
"position": {
|
| 152 |
-
"x": 1068.1904563045216,
|
| 153 |
-
"y": 313.7040149772122
|
| 154 |
-
},
|
| 155 |
-
"type": "image",
|
| 156 |
-
"width": 398.0
|
| 157 |
-
},
|
| 158 |
-
{
|
| 159 |
-
"data": {
|
| 160 |
-
"__execution_delay": null,
|
| 161 |
-
"collapsed": true,
|
| 162 |
-
"display": null,
|
| 163 |
-
"error": null,
|
| 164 |
-
"input_metadata": null,
|
| 165 |
-
"meta": {
|
| 166 |
-
"inputs": [
|
| 167 |
-
{
|
| 168 |
-
"name": "image",
|
| 169 |
-
"position": "left",
|
| 170 |
-
"type": {
|
| 171 |
-
"type": "<module 'PIL.Image' from '/media/nvme/darabos/lynxkite-2024/.venv/lib/python3.11/site-packages/PIL/Image.py'>"
|
| 172 |
-
}
|
| 173 |
-
}
|
| 174 |
-
],
|
| 175 |
-
"name": "To grayscale",
|
| 176 |
-
"outputs": [
|
| 177 |
-
{
|
| 178 |
-
"name": "output",
|
| 179 |
-
"position": "right",
|
| 180 |
-
"type": {
|
| 181 |
-
"type": "None"
|
| 182 |
-
}
|
| 183 |
-
}
|
| 184 |
-
],
|
| 185 |
-
"params": [],
|
| 186 |
-
"type": "basic"
|
| 187 |
-
},
|
| 188 |
-
"params": {},
|
| 189 |
-
"status": "done",
|
| 190 |
-
"title": "To grayscale"
|
| 191 |
-
},
|
| 192 |
-
"dragHandle": ".bg-primary",
|
| 193 |
-
"height": 200.0,
|
| 194 |
-
"id": "To grayscale 1",
|
| 195 |
-
"parentId": null,
|
| 196 |
-
"position": {
|
| 197 |
-
"x": 788.18031953735,
|
| 198 |
-
"y": 541.1434137066244
|
| 199 |
-
},
|
| 200 |
-
"type": "basic",
|
| 201 |
-
"width": 200.0
|
| 202 |
-
},
|
| 203 |
-
{
|
| 204 |
-
"data": {
|
| 205 |
-
"__execution_delay": 0.0,
|
| 206 |
-
"collapsed": null,
|
| 207 |
-
"display": null,
|
| 208 |
-
"error": null,
|
| 209 |
-
"input_metadata": null,
|
| 210 |
-
"meta": {
|
| 211 |
-
"inputs": [
|
| 212 |
-
{
|
| 213 |
-
"name": "image",
|
| 214 |
-
"position": "left",
|
| 215 |
-
"type": {
|
| 216 |
-
"type": "<module 'PIL.Image' from '/media/nvme/darabos/lynxkite-2024/.venv/lib/python3.11/site-packages/PIL/Image.py'>"
|
| 217 |
-
}
|
| 218 |
-
}
|
| 219 |
-
],
|
| 220 |
-
"name": "Blur",
|
| 221 |
-
"outputs": [
|
| 222 |
-
{
|
| 223 |
-
"name": "output",
|
| 224 |
-
"position": "right",
|
| 225 |
-
"type": {
|
| 226 |
-
"type": "None"
|
| 227 |
-
}
|
| 228 |
-
}
|
| 229 |
-
],
|
| 230 |
-
"params": [
|
| 231 |
-
{
|
| 232 |
-
"default": 5.0,
|
| 233 |
-
"name": "radius",
|
| 234 |
-
"type": {
|
| 235 |
-
"type": "<class 'float'>"
|
| 236 |
-
}
|
| 237 |
-
}
|
| 238 |
-
],
|
| 239 |
-
"type": "basic"
|
| 240 |
-
},
|
| 241 |
-
"params": {
|
| 242 |
-
"radius": "5"
|
| 243 |
-
},
|
| 244 |
-
"status": "done",
|
| 245 |
-
"title": "Blur"
|
| 246 |
-
},
|
| 247 |
-
"dragHandle": ".bg-primary",
|
| 248 |
-
"height": 200.0,
|
| 249 |
-
"id": "Blur 1",
|
| 250 |
-
"parentId": null,
|
| 251 |
-
"position": {
|
| 252 |
-
"x": 505.15961556359304,
|
| 253 |
-
"y": 539.8477981917164
|
| 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 |
-
{
|
| 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 |
-
{
|
| 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/Image table.lynxkite.json
DELETED
|
@@ -1,364 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"edges": [
|
| 3 |
-
{
|
| 4 |
-
"id": "Example image table 1 View tables 1",
|
| 5 |
-
"source": "Example image table 1",
|
| 6 |
-
"sourceHandle": "output",
|
| 7 |
-
"target": "View tables 1",
|
| 8 |
-
"targetHandle": "bundle"
|
| 9 |
-
},
|
| 10 |
-
{
|
| 11 |
-
"id": "Import CSV 1 Draw molecules 1",
|
| 12 |
-
"source": "Import CSV 1",
|
| 13 |
-
"sourceHandle": "output",
|
| 14 |
-
"target": "Draw molecules 1",
|
| 15 |
-
"targetHandle": "df"
|
| 16 |
-
},
|
| 17 |
-
{
|
| 18 |
-
"id": "Draw molecules 1 View tables 2",
|
| 19 |
-
"source": "Draw molecules 1",
|
| 20 |
-
"sourceHandle": "output",
|
| 21 |
-
"target": "View tables 2",
|
| 22 |
-
"targetHandle": "bundle"
|
| 23 |
-
}
|
| 24 |
-
],
|
| 25 |
-
"env": "LynxKite Graph Analytics",
|
| 26 |
-
"nodes": [
|
| 27 |
-
{
|
| 28 |
-
"data": {
|
| 29 |
-
"__execution_delay": null,
|
| 30 |
-
"collapsed": false,
|
| 31 |
-
"display": null,
|
| 32 |
-
"error": null,
|
| 33 |
-
"input_metadata": [],
|
| 34 |
-
"meta": {
|
| 35 |
-
"color": "orange",
|
| 36 |
-
"inputs": [],
|
| 37 |
-
"name": "Example image table",
|
| 38 |
-
"outputs": [
|
| 39 |
-
{
|
| 40 |
-
"name": "output",
|
| 41 |
-
"position": "right",
|
| 42 |
-
"type": {
|
| 43 |
-
"type": "None"
|
| 44 |
-
}
|
| 45 |
-
}
|
| 46 |
-
],
|
| 47 |
-
"params": [],
|
| 48 |
-
"type": "basic"
|
| 49 |
-
},
|
| 50 |
-
"params": {},
|
| 51 |
-
"status": "done",
|
| 52 |
-
"title": "Example image table"
|
| 53 |
-
},
|
| 54 |
-
"dragHandle": ".bg-primary",
|
| 55 |
-
"height": 200.0,
|
| 56 |
-
"id": "Example image table 1",
|
| 57 |
-
"position": {
|
| 58 |
-
"x": 356.1935187064265,
|
| 59 |
-
"y": 224.8472733628614
|
| 60 |
-
},
|
| 61 |
-
"type": "basic",
|
| 62 |
-
"width": 280.0
|
| 63 |
-
},
|
| 64 |
-
{
|
| 65 |
-
"data": {
|
| 66 |
-
"display": {
|
| 67 |
-
"dataframes": {
|
| 68 |
-
"df": {
|
| 69 |
-
"columns": [
|
| 70 |
-
"names",
|
| 71 |
-
"images"
|
| 72 |
-
],
|
| 73 |
-
"data": [
|
| 74 |
-
[
|
| 75 |
-
"svg",
|
| 76 |
-
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\" enable-background=\"new 0 0 64 64\"><path d=\"M56 2 18.8 42.909 8 34.729 2 34.729 18.8 62 62 2z\"/></svg>"
|
| 77 |
-
],
|
| 78 |
-
[
|
| 79 |
-
"data",
|
| 80 |
-
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNjQgNjQiPjxwYXRoIGQ9Ik01NiAyIDE4LjggNDIuOTA5IDggMzQuNzI5IDIgMzQuNzI5IDE4LjggNjIgNjIgMnoiLz48L3N2Zz4="
|
| 81 |
-
],
|
| 82 |
-
[
|
| 83 |
-
"http",
|
| 84 |
-
"https://upload.wikimedia.org/wikipedia/commons/2/2e/Emojione_BW_2714.svg"
|
| 85 |
-
]
|
| 86 |
-
]
|
| 87 |
-
}
|
| 88 |
-
},
|
| 89 |
-
"other": {},
|
| 90 |
-
"relations": []
|
| 91 |
-
},
|
| 92 |
-
"error": null,
|
| 93 |
-
"input_metadata": [
|
| 94 |
-
{
|
| 95 |
-
"dataframes": {
|
| 96 |
-
"df": {
|
| 97 |
-
"columns": [
|
| 98 |
-
"images",
|
| 99 |
-
"names"
|
| 100 |
-
]
|
| 101 |
-
}
|
| 102 |
-
},
|
| 103 |
-
"other": {},
|
| 104 |
-
"relations": []
|
| 105 |
-
}
|
| 106 |
-
],
|
| 107 |
-
"meta": {
|
| 108 |
-
"color": "orange",
|
| 109 |
-
"inputs": [
|
| 110 |
-
{
|
| 111 |
-
"name": "bundle",
|
| 112 |
-
"position": "left",
|
| 113 |
-
"type": {
|
| 114 |
-
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
| 115 |
-
}
|
| 116 |
-
}
|
| 117 |
-
],
|
| 118 |
-
"name": "View tables",
|
| 119 |
-
"outputs": [],
|
| 120 |
-
"params": [
|
| 121 |
-
{
|
| 122 |
-
"default": 100,
|
| 123 |
-
"name": "limit",
|
| 124 |
-
"type": {
|
| 125 |
-
"type": "<class 'int'>"
|
| 126 |
-
}
|
| 127 |
-
}
|
| 128 |
-
],
|
| 129 |
-
"type": "table_view"
|
| 130 |
-
},
|
| 131 |
-
"params": {
|
| 132 |
-
"limit": 100.0
|
| 133 |
-
},
|
| 134 |
-
"status": "done",
|
| 135 |
-
"title": "View tables"
|
| 136 |
-
},
|
| 137 |
-
"dragHandle": ".bg-primary",
|
| 138 |
-
"height": 440.0,
|
| 139 |
-
"id": "View tables 1",
|
| 140 |
-
"position": {
|
| 141 |
-
"x": 757.4687936995374,
|
| 142 |
-
"y": 116.39895719598661
|
| 143 |
-
},
|
| 144 |
-
"type": "table_view",
|
| 145 |
-
"width": 376.0
|
| 146 |
-
},
|
| 147 |
-
{
|
| 148 |
-
"data": {
|
| 149 |
-
"__execution_delay": 0.0,
|
| 150 |
-
"collapsed": null,
|
| 151 |
-
"display": null,
|
| 152 |
-
"error": null,
|
| 153 |
-
"input_metadata": [],
|
| 154 |
-
"meta": {
|
| 155 |
-
"color": "orange",
|
| 156 |
-
"inputs": [],
|
| 157 |
-
"name": "Import CSV",
|
| 158 |
-
"outputs": [
|
| 159 |
-
{
|
| 160 |
-
"name": "output",
|
| 161 |
-
"position": "right",
|
| 162 |
-
"type": {
|
| 163 |
-
"type": "None"
|
| 164 |
-
}
|
| 165 |
-
}
|
| 166 |
-
],
|
| 167 |
-
"params": [
|
| 168 |
-
{
|
| 169 |
-
"default": null,
|
| 170 |
-
"name": "filename",
|
| 171 |
-
"type": {
|
| 172 |
-
"type": "<class 'str'>"
|
| 173 |
-
}
|
| 174 |
-
},
|
| 175 |
-
{
|
| 176 |
-
"default": "<from file>",
|
| 177 |
-
"name": "columns",
|
| 178 |
-
"type": {
|
| 179 |
-
"type": "<class 'str'>"
|
| 180 |
-
}
|
| 181 |
-
},
|
| 182 |
-
{
|
| 183 |
-
"default": "<auto>",
|
| 184 |
-
"name": "separator",
|
| 185 |
-
"type": {
|
| 186 |
-
"type": "<class 'str'>"
|
| 187 |
-
}
|
| 188 |
-
}
|
| 189 |
-
],
|
| 190 |
-
"type": "basic"
|
| 191 |
-
},
|
| 192 |
-
"params": {
|
| 193 |
-
"columns": "<from file>",
|
| 194 |
-
"filename": "uploads/molecules2.csv",
|
| 195 |
-
"separator": "<auto>"
|
| 196 |
-
},
|
| 197 |
-
"status": "done",
|
| 198 |
-
"title": "Import CSV"
|
| 199 |
-
},
|
| 200 |
-
"dragHandle": ".bg-primary",
|
| 201 |
-
"height": 339.0,
|
| 202 |
-
"id": "Import CSV 1",
|
| 203 |
-
"position": {
|
| 204 |
-
"x": 13.802068621055497,
|
| 205 |
-
"y": -269.65065144888104
|
| 206 |
-
},
|
| 207 |
-
"type": "basic",
|
| 208 |
-
"width": 296.0
|
| 209 |
-
},
|
| 210 |
-
{
|
| 211 |
-
"data": {
|
| 212 |
-
"display": {
|
| 213 |
-
"dataframes": {
|
| 214 |
-
"df": {
|
| 215 |
-
"columns": [
|
| 216 |
-
"name",
|
| 217 |
-
"smiles",
|
| 218 |
-
"image"
|
| 219 |
-
],
|
| 220 |
-
"data": [
|
| 221 |
-
[
|
| 222 |
-
"ciprofloxacin",
|
| 223 |
-
"C1CNCCN1c(c2)c(F)cc3c2N(C4CC4)C=C(C3=O)C(=O)O",
|
| 224 |
-
"data:image/png;base64,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"
|
| 225 |
-
],
|
| 226 |
-
[
|
| 227 |
-
"caffeine",
|
| 228 |
-
"CN1C=NC2=C1C(=O)N(C(=O)N2C)C",
|
| 229 |
-
"data:image/png;base64,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"
|
| 230 |
-
],
|
| 231 |
-
[
|
| 232 |
-
"\u03b1-d-glucopyranose",
|
| 233 |
-
"C([C@@H]1[C@H]([C@@H]([C@H]([C@H](O1)O)O)O)O)O",
|
| 234 |
-
"data:image/png;base64,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"
|
| 235 |
-
]
|
| 236 |
-
]
|
| 237 |
-
}
|
| 238 |
-
},
|
| 239 |
-
"other": {},
|
| 240 |
-
"relations": []
|
| 241 |
-
},
|
| 242 |
-
"error": null,
|
| 243 |
-
"input_metadata": [
|
| 244 |
-
{
|
| 245 |
-
"dataframes": {
|
| 246 |
-
"df": {
|
| 247 |
-
"columns": [
|
| 248 |
-
"image",
|
| 249 |
-
"name",
|
| 250 |
-
"smiles"
|
| 251 |
-
]
|
| 252 |
-
}
|
| 253 |
-
},
|
| 254 |
-
"other": {},
|
| 255 |
-
"relations": []
|
| 256 |
-
}
|
| 257 |
-
],
|
| 258 |
-
"meta": {
|
| 259 |
-
"color": "orange",
|
| 260 |
-
"inputs": [
|
| 261 |
-
{
|
| 262 |
-
"name": "bundle",
|
| 263 |
-
"position": "left",
|
| 264 |
-
"type": {
|
| 265 |
-
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
| 266 |
-
}
|
| 267 |
-
}
|
| 268 |
-
],
|
| 269 |
-
"name": "View tables",
|
| 270 |
-
"outputs": [],
|
| 271 |
-
"params": [
|
| 272 |
-
{
|
| 273 |
-
"default": 100,
|
| 274 |
-
"name": "limit",
|
| 275 |
-
"type": {
|
| 276 |
-
"type": "<class 'int'>"
|
| 277 |
-
}
|
| 278 |
-
}
|
| 279 |
-
],
|
| 280 |
-
"type": "table_view"
|
| 281 |
-
},
|
| 282 |
-
"params": {
|
| 283 |
-
"limit": 100.0
|
| 284 |
-
},
|
| 285 |
-
"status": "planned",
|
| 286 |
-
"title": "View tables"
|
| 287 |
-
},
|
| 288 |
-
"dragHandle": ".bg-primary",
|
| 289 |
-
"height": 418.0,
|
| 290 |
-
"id": "View tables 2",
|
| 291 |
-
"position": {
|
| 292 |
-
"x": 815.4121289519509,
|
| 293 |
-
"y": -330.8232285057863
|
| 294 |
-
},
|
| 295 |
-
"type": "table_view",
|
| 296 |
-
"width": 1116.0
|
| 297 |
-
},
|
| 298 |
-
{
|
| 299 |
-
"data": {
|
| 300 |
-
"__execution_delay": 0.0,
|
| 301 |
-
"collapsed": null,
|
| 302 |
-
"display": null,
|
| 303 |
-
"error": "module 'rdkit.Chem' has no attribute 'Draw'",
|
| 304 |
-
"input_metadata": [
|
| 305 |
-
{}
|
| 306 |
-
],
|
| 307 |
-
"meta": {
|
| 308 |
-
"color": "orange",
|
| 309 |
-
"inputs": [
|
| 310 |
-
{
|
| 311 |
-
"name": "df",
|
| 312 |
-
"position": "left",
|
| 313 |
-
"type": {
|
| 314 |
-
"type": "<class 'pandas.core.frame.DataFrame'>"
|
| 315 |
-
}
|
| 316 |
-
}
|
| 317 |
-
],
|
| 318 |
-
"name": "Draw molecules",
|
| 319 |
-
"outputs": [
|
| 320 |
-
{
|
| 321 |
-
"name": "output",
|
| 322 |
-
"position": "right",
|
| 323 |
-
"type": {
|
| 324 |
-
"type": "None"
|
| 325 |
-
}
|
| 326 |
-
}
|
| 327 |
-
],
|
| 328 |
-
"params": [
|
| 329 |
-
{
|
| 330 |
-
"default": null,
|
| 331 |
-
"name": "smiles_column",
|
| 332 |
-
"type": {
|
| 333 |
-
"type": "<class 'str'>"
|
| 334 |
-
}
|
| 335 |
-
},
|
| 336 |
-
{
|
| 337 |
-
"default": "image",
|
| 338 |
-
"name": "image_column",
|
| 339 |
-
"type": {
|
| 340 |
-
"type": "<class 'str'>"
|
| 341 |
-
}
|
| 342 |
-
}
|
| 343 |
-
],
|
| 344 |
-
"type": "basic"
|
| 345 |
-
},
|
| 346 |
-
"params": {
|
| 347 |
-
"image_column": "image",
|
| 348 |
-
"smiles_column": "smiles"
|
| 349 |
-
},
|
| 350 |
-
"status": "done",
|
| 351 |
-
"title": "Draw molecules"
|
| 352 |
-
},
|
| 353 |
-
"dragHandle": ".bg-primary",
|
| 354 |
-
"height": 296.0,
|
| 355 |
-
"id": "Draw molecules 1",
|
| 356 |
-
"position": {
|
| 357 |
-
"x": 351.1956913898301,
|
| 358 |
-
"y": -235.00831568554486
|
| 359 |
-
},
|
| 360 |
-
"type": "basic",
|
| 361 |
-
"width": 212.0
|
| 362 |
-
}
|
| 363 |
-
]
|
| 364 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/Model definition.lynxkite.json
DELETED
|
@@ -1,671 +0,0 @@
|
|
| 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 |
-
"categories": [],
|
| 85 |
-
"color": "green",
|
| 86 |
-
"doc": null,
|
| 87 |
-
"id": "Optimizer",
|
| 88 |
-
"inputs": [
|
| 89 |
-
{
|
| 90 |
-
"name": "loss",
|
| 91 |
-
"position": "bottom",
|
| 92 |
-
"type": {
|
| 93 |
-
"type": "tensor"
|
| 94 |
-
}
|
| 95 |
-
}
|
| 96 |
-
],
|
| 97 |
-
"name": "Optimizer",
|
| 98 |
-
"outputs": [],
|
| 99 |
-
"params": [
|
| 100 |
-
{
|
| 101 |
-
"default": "AdamW",
|
| 102 |
-
"name": "type",
|
| 103 |
-
"type": {
|
| 104 |
-
"enum": [
|
| 105 |
-
"AdamW",
|
| 106 |
-
"Adafactor",
|
| 107 |
-
"Adagrad",
|
| 108 |
-
"SGD",
|
| 109 |
-
"Lion",
|
| 110 |
-
"Paged AdamW",
|
| 111 |
-
"Galore AdamW"
|
| 112 |
-
]
|
| 113 |
-
}
|
| 114 |
-
},
|
| 115 |
-
{
|
| 116 |
-
"default": 0.0001,
|
| 117 |
-
"name": "lr",
|
| 118 |
-
"type": {
|
| 119 |
-
"type": "<class 'float'>"
|
| 120 |
-
}
|
| 121 |
-
}
|
| 122 |
-
],
|
| 123 |
-
"type": "basic"
|
| 124 |
-
},
|
| 125 |
-
"op_id": "Optimizer",
|
| 126 |
-
"params": {
|
| 127 |
-
"lr": "0.1",
|
| 128 |
-
"type": "SGD"
|
| 129 |
-
},
|
| 130 |
-
"status": "done",
|
| 131 |
-
"title": "Optimizer"
|
| 132 |
-
},
|
| 133 |
-
"dragHandle": ".drag-handle",
|
| 134 |
-
"height": 250.0,
|
| 135 |
-
"id": "Optimizer 2",
|
| 136 |
-
"position": {
|
| 137 |
-
"x": 359.75221367487865,
|
| 138 |
-
"y": -1150.2183224762075
|
| 139 |
-
},
|
| 140 |
-
"type": "basic",
|
| 141 |
-
"width": 232.0
|
| 142 |
-
},
|
| 143 |
-
{
|
| 144 |
-
"data": {
|
| 145 |
-
"__execution_delay": 0.0,
|
| 146 |
-
"collapsed": null,
|
| 147 |
-
"display": null,
|
| 148 |
-
"error": null,
|
| 149 |
-
"input_metadata": null,
|
| 150 |
-
"meta": {
|
| 151 |
-
"categories": [],
|
| 152 |
-
"color": "orange",
|
| 153 |
-
"doc": null,
|
| 154 |
-
"id": "Activation",
|
| 155 |
-
"inputs": [
|
| 156 |
-
{
|
| 157 |
-
"name": "x",
|
| 158 |
-
"position": "bottom",
|
| 159 |
-
"type": {
|
| 160 |
-
"type": "<class 'inspect._empty'>"
|
| 161 |
-
}
|
| 162 |
-
}
|
| 163 |
-
],
|
| 164 |
-
"name": "Activation",
|
| 165 |
-
"outputs": [
|
| 166 |
-
{
|
| 167 |
-
"name": "output",
|
| 168 |
-
"position": "top",
|
| 169 |
-
"type": {
|
| 170 |
-
"type": "None"
|
| 171 |
-
}
|
| 172 |
-
}
|
| 173 |
-
],
|
| 174 |
-
"params": [
|
| 175 |
-
{
|
| 176 |
-
"default": "ReLU",
|
| 177 |
-
"name": "type",
|
| 178 |
-
"type": {
|
| 179 |
-
"enum": [
|
| 180 |
-
"ELU",
|
| 181 |
-
"GELU",
|
| 182 |
-
"LeakyReLU",
|
| 183 |
-
"Mish",
|
| 184 |
-
"PReLU",
|
| 185 |
-
"ReLU",
|
| 186 |
-
"Sigmoid",
|
| 187 |
-
"SiLU",
|
| 188 |
-
"Softplus",
|
| 189 |
-
"Tanh"
|
| 190 |
-
]
|
| 191 |
-
}
|
| 192 |
-
}
|
| 193 |
-
],
|
| 194 |
-
"type": "basic"
|
| 195 |
-
},
|
| 196 |
-
"op_id": "Activation",
|
| 197 |
-
"params": {
|
| 198 |
-
"type": "LeakyReLU"
|
| 199 |
-
},
|
| 200 |
-
"status": "done",
|
| 201 |
-
"title": "Activation"
|
| 202 |
-
},
|
| 203 |
-
"dragHandle": ".drag-handle",
|
| 204 |
-
"height": 200.0,
|
| 205 |
-
"id": "Activation 1",
|
| 206 |
-
"position": {
|
| 207 |
-
"x": 99.77615018185415,
|
| 208 |
-
"y": -249.43925929074078
|
| 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": null,
|
| 219 |
-
"input_metadata": null,
|
| 220 |
-
"meta": {
|
| 221 |
-
"categories": [],
|
| 222 |
-
"color": "gray",
|
| 223 |
-
"doc": null,
|
| 224 |
-
"id": "Input: tensor",
|
| 225 |
-
"inputs": [],
|
| 226 |
-
"name": "Input: tensor",
|
| 227 |
-
"outputs": [
|
| 228 |
-
{
|
| 229 |
-
"name": "output",
|
| 230 |
-
"position": "top",
|
| 231 |
-
"type": {
|
| 232 |
-
"type": "tensor"
|
| 233 |
-
}
|
| 234 |
-
}
|
| 235 |
-
],
|
| 236 |
-
"params": [
|
| 237 |
-
{
|
| 238 |
-
"default": null,
|
| 239 |
-
"name": "name",
|
| 240 |
-
"type": {
|
| 241 |
-
"type": "None"
|
| 242 |
-
}
|
| 243 |
-
}
|
| 244 |
-
],
|
| 245 |
-
"type": "basic"
|
| 246 |
-
},
|
| 247 |
-
"op_id": "Input: tensor",
|
| 248 |
-
"params": {
|
| 249 |
-
"name": "Y"
|
| 250 |
-
},
|
| 251 |
-
"status": "done",
|
| 252 |
-
"title": "Input: tensor"
|
| 253 |
-
},
|
| 254 |
-
"dragHandle": ".drag-handle",
|
| 255 |
-
"height": 200.0,
|
| 256 |
-
"id": "Input: tensor 3",
|
| 257 |
-
"position": {
|
| 258 |
-
"x": 454.7823474758749,
|
| 259 |
-
"y": -212.0655794519241
|
| 260 |
-
},
|
| 261 |
-
"type": "basic",
|
| 262 |
-
"width": 200.0
|
| 263 |
-
},
|
| 264 |
-
{
|
| 265 |
-
"data": {
|
| 266 |
-
"__execution_delay": null,
|
| 267 |
-
"collapsed": true,
|
| 268 |
-
"display": null,
|
| 269 |
-
"error": null,
|
| 270 |
-
"input_metadata": null,
|
| 271 |
-
"meta": {
|
| 272 |
-
"categories": [],
|
| 273 |
-
"color": "orange",
|
| 274 |
-
"doc": null,
|
| 275 |
-
"id": "MSE loss",
|
| 276 |
-
"inputs": [
|
| 277 |
-
{
|
| 278 |
-
"name": "x",
|
| 279 |
-
"position": "bottom",
|
| 280 |
-
"type": {
|
| 281 |
-
"type": "<class 'inspect._empty'>"
|
| 282 |
-
}
|
| 283 |
-
},
|
| 284 |
-
{
|
| 285 |
-
"name": "y",
|
| 286 |
-
"position": "bottom",
|
| 287 |
-
"type": {
|
| 288 |
-
"type": "<class 'inspect._empty'>"
|
| 289 |
-
}
|
| 290 |
-
}
|
| 291 |
-
],
|
| 292 |
-
"name": "MSE loss",
|
| 293 |
-
"outputs": [
|
| 294 |
-
{
|
| 295 |
-
"name": "output",
|
| 296 |
-
"position": "top",
|
| 297 |
-
"type": {
|
| 298 |
-
"type": "None"
|
| 299 |
-
}
|
| 300 |
-
}
|
| 301 |
-
],
|
| 302 |
-
"params": [],
|
| 303 |
-
"type": "basic"
|
| 304 |
-
},
|
| 305 |
-
"op_id": "MSE loss",
|
| 306 |
-
"params": {},
|
| 307 |
-
"status": "done",
|
| 308 |
-
"title": "MSE loss"
|
| 309 |
-
},
|
| 310 |
-
"dragHandle": ".drag-handle",
|
| 311 |
-
"height": 200.0,
|
| 312 |
-
"id": "MSE loss 2",
|
| 313 |
-
"position": {
|
| 314 |
-
"x": 375.21624462193034,
|
| 315 |
-
"y": -721.0552036572305
|
| 316 |
-
},
|
| 317 |
-
"type": "basic",
|
| 318 |
-
"width": 200.0
|
| 319 |
-
},
|
| 320 |
-
{
|
| 321 |
-
"data": {
|
| 322 |
-
"__execution_delay": 0.0,
|
| 323 |
-
"collapsed": null,
|
| 324 |
-
"display": null,
|
| 325 |
-
"error": null,
|
| 326 |
-
"input_metadata": null,
|
| 327 |
-
"meta": {
|
| 328 |
-
"categories": [],
|
| 329 |
-
"color": "orange",
|
| 330 |
-
"doc": null,
|
| 331 |
-
"id": "Repeat",
|
| 332 |
-
"inputs": [
|
| 333 |
-
{
|
| 334 |
-
"name": "input",
|
| 335 |
-
"position": "top",
|
| 336 |
-
"type": {
|
| 337 |
-
"type": "tensor"
|
| 338 |
-
}
|
| 339 |
-
}
|
| 340 |
-
],
|
| 341 |
-
"name": "Repeat",
|
| 342 |
-
"outputs": [
|
| 343 |
-
{
|
| 344 |
-
"name": "output",
|
| 345 |
-
"position": "bottom",
|
| 346 |
-
"type": {
|
| 347 |
-
"type": "tensor"
|
| 348 |
-
}
|
| 349 |
-
}
|
| 350 |
-
],
|
| 351 |
-
"params": [
|
| 352 |
-
{
|
| 353 |
-
"default": 1.0,
|
| 354 |
-
"name": "times",
|
| 355 |
-
"type": {
|
| 356 |
-
"type": "<class 'int'>"
|
| 357 |
-
}
|
| 358 |
-
},
|
| 359 |
-
{
|
| 360 |
-
"default": false,
|
| 361 |
-
"name": "same_weights",
|
| 362 |
-
"type": {
|
| 363 |
-
"type": "<class 'bool'>"
|
| 364 |
-
}
|
| 365 |
-
}
|
| 366 |
-
],
|
| 367 |
-
"type": "basic"
|
| 368 |
-
},
|
| 369 |
-
"op_id": "Repeat",
|
| 370 |
-
"params": {
|
| 371 |
-
"same_weights": false,
|
| 372 |
-
"times": "2"
|
| 373 |
-
},
|
| 374 |
-
"status": "done",
|
| 375 |
-
"title": "Repeat"
|
| 376 |
-
},
|
| 377 |
-
"dragHandle": ".drag-handle",
|
| 378 |
-
"height": 200.0,
|
| 379 |
-
"id": "Repeat 1",
|
| 380 |
-
"position": {
|
| 381 |
-
"x": -210.0,
|
| 382 |
-
"y": -135.0
|
| 383 |
-
},
|
| 384 |
-
"type": "basic",
|
| 385 |
-
"width": 200.0
|
| 386 |
-
},
|
| 387 |
-
{
|
| 388 |
-
"data": {
|
| 389 |
-
"__execution_delay": 0.0,
|
| 390 |
-
"collapsed": null,
|
| 391 |
-
"display": null,
|
| 392 |
-
"error": null,
|
| 393 |
-
"input_metadata": null,
|
| 394 |
-
"meta": {
|
| 395 |
-
"categories": [],
|
| 396 |
-
"color": "blue",
|
| 397 |
-
"doc": null,
|
| 398 |
-
"id": "Linear",
|
| 399 |
-
"inputs": [
|
| 400 |
-
{
|
| 401 |
-
"name": "x",
|
| 402 |
-
"position": "bottom",
|
| 403 |
-
"type": {
|
| 404 |
-
"type": "<class 'inspect._empty'>"
|
| 405 |
-
}
|
| 406 |
-
}
|
| 407 |
-
],
|
| 408 |
-
"name": "Linear",
|
| 409 |
-
"outputs": [
|
| 410 |
-
{
|
| 411 |
-
"name": "output",
|
| 412 |
-
"position": "top",
|
| 413 |
-
"type": {
|
| 414 |
-
"type": "None"
|
| 415 |
-
}
|
| 416 |
-
}
|
| 417 |
-
],
|
| 418 |
-
"params": [
|
| 419 |
-
{
|
| 420 |
-
"default": 1024.0,
|
| 421 |
-
"name": "output_dim",
|
| 422 |
-
"type": {
|
| 423 |
-
"type": "<class 'int'>"
|
| 424 |
-
}
|
| 425 |
-
}
|
| 426 |
-
],
|
| 427 |
-
"type": "basic"
|
| 428 |
-
},
|
| 429 |
-
"op_id": "Linear",
|
| 430 |
-
"params": {
|
| 431 |
-
"output_dim": "4"
|
| 432 |
-
},
|
| 433 |
-
"status": "done",
|
| 434 |
-
"title": "Linear"
|
| 435 |
-
},
|
| 436 |
-
"dragHandle": ".drag-handle",
|
| 437 |
-
"height": 189.0,
|
| 438 |
-
"id": "Linear 1",
|
| 439 |
-
"position": {
|
| 440 |
-
"x": 98.54861342271252,
|
| 441 |
-
"y": 14.121603973834155
|
| 442 |
-
},
|
| 443 |
-
"type": "basic",
|
| 444 |
-
"width": 199.0
|
| 445 |
-
},
|
| 446 |
-
{
|
| 447 |
-
"data": {
|
| 448 |
-
"__execution_delay": 0.0,
|
| 449 |
-
"collapsed": null,
|
| 450 |
-
"display": null,
|
| 451 |
-
"error": null,
|
| 452 |
-
"input_metadata": null,
|
| 453 |
-
"meta": {
|
| 454 |
-
"categories": [],
|
| 455 |
-
"color": "gray",
|
| 456 |
-
"doc": null,
|
| 457 |
-
"id": "Input: tensor",
|
| 458 |
-
"inputs": [],
|
| 459 |
-
"name": "Input: tensor",
|
| 460 |
-
"outputs": [
|
| 461 |
-
{
|
| 462 |
-
"name": "output",
|
| 463 |
-
"position": "top",
|
| 464 |
-
"type": {
|
| 465 |
-
"type": "tensor"
|
| 466 |
-
}
|
| 467 |
-
}
|
| 468 |
-
],
|
| 469 |
-
"params": [
|
| 470 |
-
{
|
| 471 |
-
"default": null,
|
| 472 |
-
"name": "name",
|
| 473 |
-
"type": {
|
| 474 |
-
"type": "None"
|
| 475 |
-
}
|
| 476 |
-
}
|
| 477 |
-
],
|
| 478 |
-
"type": "basic"
|
| 479 |
-
},
|
| 480 |
-
"op_id": "Input: tensor",
|
| 481 |
-
"params": {
|
| 482 |
-
"name": "X"
|
| 483 |
-
},
|
| 484 |
-
"status": "done",
|
| 485 |
-
"title": "Input: tensor"
|
| 486 |
-
},
|
| 487 |
-
"dragHandle": ".drag-handle",
|
| 488 |
-
"height": 200.0,
|
| 489 |
-
"id": "Input: tensor 1",
|
| 490 |
-
"position": {
|
| 491 |
-
"x": 108.75735538875443,
|
| 492 |
-
"y": 331.53404347930933
|
| 493 |
-
},
|
| 494 |
-
"type": "basic",
|
| 495 |
-
"width": 200.0
|
| 496 |
-
},
|
| 497 |
-
{
|
| 498 |
-
"data": {
|
| 499 |
-
"__execution_delay": 0.0,
|
| 500 |
-
"collapsed": null,
|
| 501 |
-
"display": null,
|
| 502 |
-
"error": null,
|
| 503 |
-
"input_metadata": null,
|
| 504 |
-
"meta": {
|
| 505 |
-
"categories": [],
|
| 506 |
-
"color": "orange",
|
| 507 |
-
"doc": null,
|
| 508 |
-
"id": "Constant vector",
|
| 509 |
-
"inputs": [],
|
| 510 |
-
"name": "Constant vector",
|
| 511 |
-
"outputs": [
|
| 512 |
-
{
|
| 513 |
-
"name": "output",
|
| 514 |
-
"position": "top",
|
| 515 |
-
"type": {
|
| 516 |
-
"type": "None"
|
| 517 |
-
}
|
| 518 |
-
}
|
| 519 |
-
],
|
| 520 |
-
"params": [
|
| 521 |
-
{
|
| 522 |
-
"default": 0.0,
|
| 523 |
-
"name": "value",
|
| 524 |
-
"type": {
|
| 525 |
-
"type": "<class 'int'>"
|
| 526 |
-
}
|
| 527 |
-
},
|
| 528 |
-
{
|
| 529 |
-
"default": 1.0,
|
| 530 |
-
"name": "size",
|
| 531 |
-
"type": {
|
| 532 |
-
"type": "<class 'int'>"
|
| 533 |
-
}
|
| 534 |
-
}
|
| 535 |
-
],
|
| 536 |
-
"type": "basic"
|
| 537 |
-
},
|
| 538 |
-
"op_id": "Constant vector",
|
| 539 |
-
"params": {
|
| 540 |
-
"size": "1",
|
| 541 |
-
"value": "1"
|
| 542 |
-
},
|
| 543 |
-
"status": "done",
|
| 544 |
-
"title": "Constant vector"
|
| 545 |
-
},
|
| 546 |
-
"dragHandle": ".drag-handle",
|
| 547 |
-
"height": 258.0,
|
| 548 |
-
"id": "Constant vector 1",
|
| 549 |
-
"position": {
|
| 550 |
-
"x": 846.2767459753351,
|
| 551 |
-
"y": -226.90556526533476
|
| 552 |
-
},
|
| 553 |
-
"type": "basic",
|
| 554 |
-
"width": 238.0
|
| 555 |
-
},
|
| 556 |
-
{
|
| 557 |
-
"data": {
|
| 558 |
-
"__execution_delay": null,
|
| 559 |
-
"collapsed": true,
|
| 560 |
-
"display": null,
|
| 561 |
-
"error": null,
|
| 562 |
-
"input_metadata": null,
|
| 563 |
-
"meta": {
|
| 564 |
-
"categories": [],
|
| 565 |
-
"color": "orange",
|
| 566 |
-
"doc": null,
|
| 567 |
-
"id": "Add",
|
| 568 |
-
"inputs": [
|
| 569 |
-
{
|
| 570 |
-
"name": "a",
|
| 571 |
-
"position": "bottom",
|
| 572 |
-
"type": {
|
| 573 |
-
"type": "<class 'inspect._empty'>"
|
| 574 |
-
}
|
| 575 |
-
},
|
| 576 |
-
{
|
| 577 |
-
"name": "b",
|
| 578 |
-
"position": "bottom",
|
| 579 |
-
"type": {
|
| 580 |
-
"type": "<class 'inspect._empty'>"
|
| 581 |
-
}
|
| 582 |
-
}
|
| 583 |
-
],
|
| 584 |
-
"name": "Add",
|
| 585 |
-
"outputs": [
|
| 586 |
-
{
|
| 587 |
-
"name": "output",
|
| 588 |
-
"position": "top",
|
| 589 |
-
"type": {
|
| 590 |
-
"type": "None"
|
| 591 |
-
}
|
| 592 |
-
}
|
| 593 |
-
],
|
| 594 |
-
"params": [],
|
| 595 |
-
"type": "basic"
|
| 596 |
-
},
|
| 597 |
-
"op_id": "Add",
|
| 598 |
-
"params": {},
|
| 599 |
-
"status": "done",
|
| 600 |
-
"title": "Add"
|
| 601 |
-
},
|
| 602 |
-
"dragHandle": ".drag-handle",
|
| 603 |
-
"height": 200.0,
|
| 604 |
-
"id": "Add 1",
|
| 605 |
-
"position": {
|
| 606 |
-
"x": 631.934390777073,
|
| 607 |
-
"y": -395.6855954439944
|
| 608 |
-
},
|
| 609 |
-
"type": "basic",
|
| 610 |
-
"width": 200.0
|
| 611 |
-
},
|
| 612 |
-
{
|
| 613 |
-
"data": {
|
| 614 |
-
"__execution_delay": null,
|
| 615 |
-
"collapsed": true,
|
| 616 |
-
"display": null,
|
| 617 |
-
"error": null,
|
| 618 |
-
"input_metadata": null,
|
| 619 |
-
"meta": {
|
| 620 |
-
"categories": [],
|
| 621 |
-
"color": "gray",
|
| 622 |
-
"doc": null,
|
| 623 |
-
"id": "Output",
|
| 624 |
-
"inputs": [
|
| 625 |
-
{
|
| 626 |
-
"name": "x",
|
| 627 |
-
"position": "bottom",
|
| 628 |
-
"type": {
|
| 629 |
-
"type": "tensor"
|
| 630 |
-
}
|
| 631 |
-
}
|
| 632 |
-
],
|
| 633 |
-
"name": "Output",
|
| 634 |
-
"outputs": [
|
| 635 |
-
{
|
| 636 |
-
"name": "x",
|
| 637 |
-
"position": "top",
|
| 638 |
-
"type": {
|
| 639 |
-
"type": "tensor"
|
| 640 |
-
}
|
| 641 |
-
}
|
| 642 |
-
],
|
| 643 |
-
"params": [
|
| 644 |
-
{
|
| 645 |
-
"default": null,
|
| 646 |
-
"name": "name",
|
| 647 |
-
"type": {
|
| 648 |
-
"type": "None"
|
| 649 |
-
}
|
| 650 |
-
}
|
| 651 |
-
],
|
| 652 |
-
"type": "basic"
|
| 653 |
-
},
|
| 654 |
-
"op_id": "Output",
|
| 655 |
-
"params": {},
|
| 656 |
-
"status": "done",
|
| 657 |
-
"title": "Output"
|
| 658 |
-
},
|
| 659 |
-
"dragHandle": ".drag-handle",
|
| 660 |
-
"height": 200.0,
|
| 661 |
-
"id": "Output 1",
|
| 662 |
-
"position": {
|
| 663 |
-
"x": 119.83887514325258,
|
| 664 |
-
"y": -453.23756095856885
|
| 665 |
-
},
|
| 666 |
-
"type": "basic",
|
| 667 |
-
"width": 200.0
|
| 668 |
-
}
|
| 669 |
-
],
|
| 670 |
-
"paused": false
|
| 671 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/Model use.lynxkite.json
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
examples/Multi-output demo.lynxkite.json
DELETED
|
@@ -1,301 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"edges": [
|
| 3 |
-
{
|
| 4 |
-
"id": "Multi-output example 1 one View tables 1 bundle",
|
| 5 |
-
"source": "Multi-output example 1",
|
| 6 |
-
"sourceHandle": "one",
|
| 7 |
-
"target": "View tables 1",
|
| 8 |
-
"targetHandle": "bundle"
|
| 9 |
-
},
|
| 10 |
-
{
|
| 11 |
-
"id": "Multi-output example 1 two View tables 2 bundle",
|
| 12 |
-
"source": "Multi-output example 1",
|
| 13 |
-
"sourceHandle": "two",
|
| 14 |
-
"target": "View tables 2",
|
| 15 |
-
"targetHandle": "bundle"
|
| 16 |
-
}
|
| 17 |
-
],
|
| 18 |
-
"env": "LynxKite Graph Analytics",
|
| 19 |
-
"nodes": [
|
| 20 |
-
{
|
| 21 |
-
"data": {
|
| 22 |
-
"__execution_delay": 0.0,
|
| 23 |
-
"collapsed": false,
|
| 24 |
-
"display": null,
|
| 25 |
-
"error": null,
|
| 26 |
-
"input_metadata": [],
|
| 27 |
-
"meta": {
|
| 28 |
-
"categories": [
|
| 29 |
-
"Examples"
|
| 30 |
-
],
|
| 31 |
-
"color": "orange",
|
| 32 |
-
"doc": [
|
| 33 |
-
{
|
| 34 |
-
"kind": "text",
|
| 35 |
-
"value": "Returns two outputs. Also demonstrates Numpy-style docstrings."
|
| 36 |
-
},
|
| 37 |
-
{
|
| 38 |
-
"kind": "parameters",
|
| 39 |
-
"value": [
|
| 40 |
-
{
|
| 41 |
-
"annotation": "int",
|
| 42 |
-
"description": "Number of elements in output \"one\".",
|
| 43 |
-
"name": "a_limit"
|
| 44 |
-
},
|
| 45 |
-
{
|
| 46 |
-
"annotation": "int",
|
| 47 |
-
"description": "Number of elements in output \"two\".",
|
| 48 |
-
"name": "b_limit"
|
| 49 |
-
}
|
| 50 |
-
]
|
| 51 |
-
},
|
| 52 |
-
{
|
| 53 |
-
"kind": "returns",
|
| 54 |
-
"value": [
|
| 55 |
-
{
|
| 56 |
-
"annotation": "A dict with two DataFrames in it.",
|
| 57 |
-
"description": "",
|
| 58 |
-
"name": ""
|
| 59 |
-
}
|
| 60 |
-
]
|
| 61 |
-
}
|
| 62 |
-
],
|
| 63 |
-
"id": "Examples > Multi-output example",
|
| 64 |
-
"inputs": [],
|
| 65 |
-
"name": "Multi-output example",
|
| 66 |
-
"outputs": [
|
| 67 |
-
{
|
| 68 |
-
"name": "one",
|
| 69 |
-
"position": "right",
|
| 70 |
-
"type": {
|
| 71 |
-
"type": "None"
|
| 72 |
-
}
|
| 73 |
-
},
|
| 74 |
-
{
|
| 75 |
-
"name": "two",
|
| 76 |
-
"position": "right",
|
| 77 |
-
"type": {
|
| 78 |
-
"type": "None"
|
| 79 |
-
}
|
| 80 |
-
}
|
| 81 |
-
],
|
| 82 |
-
"params": [
|
| 83 |
-
{
|
| 84 |
-
"default": 4,
|
| 85 |
-
"name": "a_limit",
|
| 86 |
-
"type": {
|
| 87 |
-
"type": "<class 'int'>"
|
| 88 |
-
}
|
| 89 |
-
},
|
| 90 |
-
{
|
| 91 |
-
"default": 10,
|
| 92 |
-
"name": "b_limit",
|
| 93 |
-
"type": {
|
| 94 |
-
"type": "<class 'int'>"
|
| 95 |
-
}
|
| 96 |
-
}
|
| 97 |
-
],
|
| 98 |
-
"type": "basic"
|
| 99 |
-
},
|
| 100 |
-
"op_id": "Examples > Multi-output example",
|
| 101 |
-
"params": {
|
| 102 |
-
"a_limit": "2",
|
| 103 |
-
"b_limit": "10"
|
| 104 |
-
},
|
| 105 |
-
"status": "done",
|
| 106 |
-
"title": "Multi-output example"
|
| 107 |
-
},
|
| 108 |
-
"dragHandle": ".drag-handle",
|
| 109 |
-
"height": 275.0,
|
| 110 |
-
"id": "Multi-output example 1",
|
| 111 |
-
"position": {
|
| 112 |
-
"x": 86.0,
|
| 113 |
-
"y": 33.0
|
| 114 |
-
},
|
| 115 |
-
"type": "basic",
|
| 116 |
-
"width": 200.0
|
| 117 |
-
},
|
| 118 |
-
{
|
| 119 |
-
"data": {
|
| 120 |
-
"display": {
|
| 121 |
-
"dataframes": {
|
| 122 |
-
"df": {
|
| 123 |
-
"columns": [
|
| 124 |
-
"a"
|
| 125 |
-
],
|
| 126 |
-
"data": [
|
| 127 |
-
[
|
| 128 |
-
0
|
| 129 |
-
],
|
| 130 |
-
[
|
| 131 |
-
1
|
| 132 |
-
]
|
| 133 |
-
]
|
| 134 |
-
}
|
| 135 |
-
},
|
| 136 |
-
"other": {},
|
| 137 |
-
"relations": []
|
| 138 |
-
},
|
| 139 |
-
"error": null,
|
| 140 |
-
"input_metadata": [
|
| 141 |
-
{
|
| 142 |
-
"dataframes": {
|
| 143 |
-
"df": {
|
| 144 |
-
"columns": [
|
| 145 |
-
"a"
|
| 146 |
-
]
|
| 147 |
-
}
|
| 148 |
-
},
|
| 149 |
-
"other": {},
|
| 150 |
-
"relations": []
|
| 151 |
-
}
|
| 152 |
-
],
|
| 153 |
-
"meta": {
|
| 154 |
-
"categories": [],
|
| 155 |
-
"color": "orange",
|
| 156 |
-
"doc": null,
|
| 157 |
-
"id": "View tables",
|
| 158 |
-
"inputs": [
|
| 159 |
-
{
|
| 160 |
-
"name": "bundle",
|
| 161 |
-
"position": "left",
|
| 162 |
-
"type": {
|
| 163 |
-
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
| 164 |
-
}
|
| 165 |
-
}
|
| 166 |
-
],
|
| 167 |
-
"name": "View tables",
|
| 168 |
-
"outputs": [],
|
| 169 |
-
"params": [
|
| 170 |
-
{
|
| 171 |
-
"default": 100,
|
| 172 |
-
"name": "limit",
|
| 173 |
-
"type": {
|
| 174 |
-
"type": "<class 'int'>"
|
| 175 |
-
}
|
| 176 |
-
}
|
| 177 |
-
],
|
| 178 |
-
"type": "table_view"
|
| 179 |
-
},
|
| 180 |
-
"op_id": "View tables",
|
| 181 |
-
"params": {
|
| 182 |
-
"limit": 100.0
|
| 183 |
-
},
|
| 184 |
-
"status": "done",
|
| 185 |
-
"title": "View tables"
|
| 186 |
-
},
|
| 187 |
-
"dragHandle": ".drag-handle",
|
| 188 |
-
"height": 200.0,
|
| 189 |
-
"id": "View tables 1",
|
| 190 |
-
"position": {
|
| 191 |
-
"x": 485.0,
|
| 192 |
-
"y": -31.0
|
| 193 |
-
},
|
| 194 |
-
"type": "table_view",
|
| 195 |
-
"width": 200.0
|
| 196 |
-
},
|
| 197 |
-
{
|
| 198 |
-
"data": {
|
| 199 |
-
"display": {
|
| 200 |
-
"dataframes": {
|
| 201 |
-
"df": {
|
| 202 |
-
"columns": [
|
| 203 |
-
"b"
|
| 204 |
-
],
|
| 205 |
-
"data": [
|
| 206 |
-
[
|
| 207 |
-
0
|
| 208 |
-
],
|
| 209 |
-
[
|
| 210 |
-
1
|
| 211 |
-
],
|
| 212 |
-
[
|
| 213 |
-
2
|
| 214 |
-
],
|
| 215 |
-
[
|
| 216 |
-
3
|
| 217 |
-
],
|
| 218 |
-
[
|
| 219 |
-
4
|
| 220 |
-
],
|
| 221 |
-
[
|
| 222 |
-
5
|
| 223 |
-
],
|
| 224 |
-
[
|
| 225 |
-
6
|
| 226 |
-
],
|
| 227 |
-
[
|
| 228 |
-
7
|
| 229 |
-
],
|
| 230 |
-
[
|
| 231 |
-
8
|
| 232 |
-
],
|
| 233 |
-
[
|
| 234 |
-
9
|
| 235 |
-
]
|
| 236 |
-
]
|
| 237 |
-
}
|
| 238 |
-
},
|
| 239 |
-
"other": {},
|
| 240 |
-
"relations": []
|
| 241 |
-
},
|
| 242 |
-
"error": null,
|
| 243 |
-
"input_metadata": [
|
| 244 |
-
{
|
| 245 |
-
"dataframes": {
|
| 246 |
-
"df": {
|
| 247 |
-
"columns": [
|
| 248 |
-
"b"
|
| 249 |
-
]
|
| 250 |
-
}
|
| 251 |
-
},
|
| 252 |
-
"other": {},
|
| 253 |
-
"relations": []
|
| 254 |
-
}
|
| 255 |
-
],
|
| 256 |
-
"meta": {
|
| 257 |
-
"categories": [],
|
| 258 |
-
"color": "orange",
|
| 259 |
-
"doc": null,
|
| 260 |
-
"id": "View tables",
|
| 261 |
-
"inputs": [
|
| 262 |
-
{
|
| 263 |
-
"name": "bundle",
|
| 264 |
-
"position": "left",
|
| 265 |
-
"type": {
|
| 266 |
-
"type": "<class 'lynxkite_graph_analytics.core.Bundle'>"
|
| 267 |
-
}
|
| 268 |
-
}
|
| 269 |
-
],
|
| 270 |
-
"name": "View tables",
|
| 271 |
-
"outputs": [],
|
| 272 |
-
"params": [
|
| 273 |
-
{
|
| 274 |
-
"default": 100,
|
| 275 |
-
"name": "limit",
|
| 276 |
-
"type": {
|
| 277 |
-
"type": "<class 'int'>"
|
| 278 |
-
}
|
| 279 |
-
}
|
| 280 |
-
],
|
| 281 |
-
"type": "table_view"
|
| 282 |
-
},
|
| 283 |
-
"op_id": "View tables",
|
| 284 |
-
"params": {
|
| 285 |
-
"limit": 100.0
|
| 286 |
-
},
|
| 287 |
-
"status": "done",
|
| 288 |
-
"title": "View tables"
|
| 289 |
-
},
|
| 290 |
-
"dragHandle": ".drag-handle",
|
| 291 |
-
"height": 215.0,
|
| 292 |
-
"id": "View tables 2",
|
| 293 |
-
"position": {
|
| 294 |
-
"x": 480.0,
|
| 295 |
-
"y": 191.0
|
| 296 |
-
},
|
| 297 |
-
"type": "table_view",
|
| 298 |
-
"width": 225.0
|
| 299 |
-
}
|
| 300 |
-
]
|
| 301 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/NetworkX demo.lynxkite.json
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
examples/Word2vec.lynxkite.json
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
examples/fake_data.py
DELETED
|
@@ -1,21 +0,0 @@
|
|
| 1 |
-
from lynxkite.core.ops import op
|
| 2 |
-
from faker import Faker # ty: ignore[unresolved-import]
|
| 3 |
-
import pandas as pd
|
| 4 |
-
|
| 5 |
-
faker = Faker()
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
@op("LynxKite Graph Analytics", "Fake data")
|
| 9 |
-
def fake(*, n=10):
|
| 10 |
-
"""Creates a DataFrame with random-generated names and postal addresses.
|
| 11 |
-
|
| 12 |
-
Parameters:
|
| 13 |
-
n: Number of rows to create.
|
| 14 |
-
"""
|
| 15 |
-
df = pd.DataFrame(
|
| 16 |
-
{
|
| 17 |
-
"name": [faker.name() for _ in range(n)],
|
| 18 |
-
"address": [faker.address() for _ in range(n)],
|
| 19 |
-
}
|
| 20 |
-
)
|
| 21 |
-
return df
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/make_image_table.py
DELETED
|
@@ -1,11 +0,0 @@
|
|
| 1 |
-
from lynxkite.core.ops import op
|
| 2 |
-
import pandas as pd
|
| 3 |
-
import base64
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
@op("LynxKite Graph Analytics", "Example image table")
|
| 7 |
-
def make_image_table():
|
| 8 |
-
svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" enable-background="new 0 0 64 64"><path d="M56 2 18.8 42.909 8 34.729 2 34.729 18.8 62 62 2z"/></svg>'
|
| 9 |
-
data = "data:image/svg+xml;base64," + base64.b64encode(svg.encode("utf-8")).decode("utf-8")
|
| 10 |
-
http = "https://upload.wikimedia.org/wikipedia/commons/2/2e/Emojione_BW_2714.svg"
|
| 11 |
-
return pd.DataFrame({"names": ["svg", "data", "http"], "images": [svg, data, http]})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/matplotlib_example.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
| 1 |
-
# From https://matplotlib.org/stable/gallery/images_contours_and_fields/contour_corner_mask.html
|
| 2 |
-
import matplotlib.pyplot as plt
|
| 3 |
-
import numpy as np
|
| 4 |
-
from lynxkite.core.ops import op
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
@op("LynxKite Graph Analytics", "Matplotlib example", view="matplotlib")
|
| 8 |
-
def example():
|
| 9 |
-
# Data to plot.
|
| 10 |
-
x, y = np.meshgrid(np.arange(7), np.arange(10))
|
| 11 |
-
z = np.sin(0.5 * x) * np.cos(0.52 * y)
|
| 12 |
-
|
| 13 |
-
# Mask various z values.
|
| 14 |
-
mask = np.zeros_like(z, dtype=bool)
|
| 15 |
-
mask[2, 3:5] = True
|
| 16 |
-
mask[3:5, 4] = True
|
| 17 |
-
mask[7, 2] = True
|
| 18 |
-
mask[5, 0] = True
|
| 19 |
-
mask[0, 6] = True
|
| 20 |
-
z = np.ma.array(z, mask=mask)
|
| 21 |
-
print(z)
|
| 22 |
-
|
| 23 |
-
corner_masks = [False, True]
|
| 24 |
-
fig, axs = plt.subplots(ncols=2)
|
| 25 |
-
for ax, corner_mask in zip(axs, corner_masks):
|
| 26 |
-
cs = ax.contourf(x, y, z, corner_mask=corner_mask)
|
| 27 |
-
ax.contour(cs, colors="k")
|
| 28 |
-
ax.set_title(f"{corner_mask=}")
|
| 29 |
-
|
| 30 |
-
# Plot grid.
|
| 31 |
-
ax.grid(c="k", ls="-", alpha=0.3)
|
| 32 |
-
|
| 33 |
-
# Indicate masked points with red circles.
|
| 34 |
-
ax.plot(np.ma.array(x, mask=~mask), y, "ro")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/multi_output_demo.py
DELETED
|
@@ -1,24 +0,0 @@
|
|
| 1 |
-
from lynxkite.core.ops import op
|
| 2 |
-
import pandas as pd
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
@op("LynxKite Graph Analytics", "Examples", "Multi-output example", outputs=["one", "two"])
|
| 6 |
-
def multi_output(*, a_limit=4, b_limit=10):
|
| 7 |
-
"""
|
| 8 |
-
Returns two outputs. Also demonstrates Numpy-style docstrings.
|
| 9 |
-
|
| 10 |
-
Parameters
|
| 11 |
-
----------
|
| 12 |
-
a_limit : int
|
| 13 |
-
Number of elements in output "one".
|
| 14 |
-
b_limit : int
|
| 15 |
-
Number of elements in output "two".
|
| 16 |
-
|
| 17 |
-
Returns
|
| 18 |
-
-------
|
| 19 |
-
A dict with two DataFrames in it.
|
| 20 |
-
"""
|
| 21 |
-
return {
|
| 22 |
-
"one": pd.DataFrame({"a": range(a_limit)}),
|
| 23 |
-
"two": pd.DataFrame({"b": range(b_limit)}),
|
| 24 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/ode_lstm.py
DELETED
|
@@ -1,54 +0,0 @@
|
|
| 1 |
-
from lynxkite.core.ops import op_registration, LongStr
|
| 2 |
-
from lynxkite_graph_analytics.core import Bundle
|
| 3 |
-
from matplotlib import pyplot as plt
|
| 4 |
-
import numpy as np
|
| 5 |
-
import pandas as pd
|
| 6 |
-
import json
|
| 7 |
-
|
| 8 |
-
op = op_registration("LynxKite Graph Analytics")
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
@op("Drop NA")
|
| 12 |
-
def drop_na(df: pd.DataFrame):
|
| 13 |
-
return df.replace("", np.nan).dropna()
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
@op("Sort by")
|
| 17 |
-
def sort_by(df: pd.DataFrame, *, key_columns: str):
|
| 18 |
-
df = df.copy()
|
| 19 |
-
df.sort_values(
|
| 20 |
-
by=[k.strip() for k in key_columns.split(",")],
|
| 21 |
-
inplace=True,
|
| 22 |
-
ignore_index=True,
|
| 23 |
-
)
|
| 24 |
-
return df
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
@op("Group by")
|
| 28 |
-
def group_by(df: pd.DataFrame, *, key_columns: str, aggregation: LongStr):
|
| 29 |
-
key_columns = [k.strip() for k in key_columns.split(",")]
|
| 30 |
-
j = json.loads(aggregation)
|
| 31 |
-
for k, vs in j.items():
|
| 32 |
-
j[k] = [list if v == "list" else v for v in vs]
|
| 33 |
-
res = df.groupby(key_columns).agg(j).reset_index()
|
| 34 |
-
res.columns = ["_".join(col) for col in res.columns]
|
| 35 |
-
return res
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
@op("Take first element of list")
|
| 39 |
-
def take_first_element(df: pd.DataFrame, *, column: str):
|
| 40 |
-
df = df.copy()
|
| 41 |
-
df[f"{column}_first_element"] = df[column].apply(lambda x: x[0])
|
| 42 |
-
return df
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
@op("Plot time series", view="matplotlib")
|
| 46 |
-
def plot_time_series(bundle: Bundle, *, table_name: str, index: int, x_column: str, y_columns: str):
|
| 47 |
-
df = bundle.dfs[table_name]
|
| 48 |
-
y_columns = [y.strip() for y in y_columns.split(",")]
|
| 49 |
-
x = df[x_column].iloc[index]
|
| 50 |
-
for y_column in y_columns:
|
| 51 |
-
y = df[y_column].iloc[index]
|
| 52 |
-
plt.plot(x, y, "o-", label=y_column)
|
| 53 |
-
plt.xlabel(x_column)
|
| 54 |
-
plt.legend()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/requirements.txt
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
# Example of a requirements.txt file. LynxKite will automatically install anything you put here.
|
| 2 |
-
faker
|
| 3 |
-
matplotlib
|
|
|
|
|
|
|
|
|
|
|
|
examples/sql.lynxkite.json
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
examples/uploads/example-pizza.md
DELETED
|
@@ -1,136 +0,0 @@
|
|
| 1 |
-
hello
|
| 2 |
-
|
| 3 |
-
### 1. **Overview**
|
| 4 |
-
|
| 5 |
-
This document outlines the pricing structure and available options for our pizza delivery service. The goal is to provide clear guidance on the pricing tiers, additional offerings, and optional extras to ensure consistency across all locations and platforms (phone, online, in-app). All pricing is based on current market trends, food costs, and competitive analysis.
|
| 6 |
-
|
| 7 |
-
---
|
| 8 |
-
|
| 9 |
-
### 2. **Pizza Options**
|
| 10 |
-
|
| 11 |
-
#### 2.1 **Size & Base Pricing**
|
| 12 |
-
|
| 13 |
-
| Size | Diameter | Price (Cheese Pizza) |
|
| 14 |
-
|------------------|------------|----------------------|
|
| 15 |
-
| Small | 10 inches | $8.99 |
|
| 16 |
-
| Medium | 12 inches | $11.99 |
|
| 17 |
-
| Large | 14 inches | $14.99 |
|
| 18 |
-
| Extra Large | 16 inches | $17.99 |
|
| 19 |
-
|
| 20 |
-
**Note**: Cheese pizza pricing includes sauce and cheese. Toppings are additional (see section 2.3).
|
| 21 |
-
|
| 22 |
-
#### 2.2 **Crust Options**
|
| 23 |
-
|
| 24 |
-
| Crust Type | Description | Price Adjustment |
|
| 25 |
-
|------------------|------------------------------------------|------------------|
|
| 26 |
-
| Classic Hand-Tossed | Soft, airy texture | No Change |
|
| 27 |
-
| Thin & Crispy | Light and crunchy | No Change |
|
| 28 |
-
| Stuffed Crust | Filled with mozzarella | +$2.00 (M-XL) |
|
| 29 |
-
| Gluten-Free | 10" only; made with rice flour | +$2.50 (Small Only) |
|
| 30 |
-
|
| 31 |
-
---
|
| 32 |
-
|
| 33 |
-
### 3. **Toppings**
|
| 34 |
-
|
| 35 |
-
#### 3.1 **Standard Toppings**
|
| 36 |
-
**Price per topping:**
|
| 37 |
-
|
| 38 |
-
- Small: $1.00
|
| 39 |
-
- Medium: $1.50
|
| 40 |
-
- Large: $2.00
|
| 41 |
-
- Extra Large: $2.50
|
| 42 |
-
|
| 43 |
-
| Topping | Category |
|
| 44 |
-
|------------------|----------------|
|
| 45 |
-
| Pepperoni | Meat |
|
| 46 |
-
| Sausage | Meat |
|
| 47 |
-
| Mushrooms | Vegetable |
|
| 48 |
-
| Onions | Vegetable |
|
| 49 |
-
| Bell Peppers | Vegetable |
|
| 50 |
-
| Olives | Vegetable |
|
| 51 |
-
| Extra Cheese | Dairy |
|
| 52 |
-
|
| 53 |
-
#### 3.2 **Premium Toppings**
|
| 54 |
-
**Price per topping:**
|
| 55 |
-
|
| 56 |
-
- Small: $1.75
|
| 57 |
-
- Medium: $2.25
|
| 58 |
-
- Large: $2.75
|
| 59 |
-
- Extra Large: $3.25
|
| 60 |
-
|
| 61 |
-
| Topping | Category |
|
| 62 |
-
|------------------|----------------|
|
| 63 |
-
| Grilled Chicken | Meat |
|
| 64 |
-
| Bacon | Meat |
|
| 65 |
-
| Sun-Dried Tomatoes| Vegetable |
|
| 66 |
-
| Artichoke Hearts | Vegetable |
|
| 67 |
-
| Feta Cheese | Dairy |
|
| 68 |
-
| Vegan Cheese | Dairy Alternative |
|
| 69 |
-
|
| 70 |
-
---
|
| 71 |
-
|
| 72 |
-
### 4. **Specialty Pizzas**
|
| 73 |
-
|
| 74 |
-
Specialty pizzas include a combination of premium toppings and are available in all sizes. Prices below are for Medium size, with additional costs for upgrading to larger sizes.
|
| 75 |
-
|
| 76 |
-
| Pizza Name | Description | Price (Medium) |
|
| 77 |
-
|----------------------|----------------------------------------------------|-----------------|
|
| 78 |
-
| Meat Lover’s | Pepperoni, sausage, bacon, ham | $16.99 |
|
| 79 |
-
| Veggie Delight | Mushrooms, bell peppers, onions, olives | $14.99 |
|
| 80 |
-
| BBQ Chicken | BBQ sauce, grilled chicken, red onions, cilantro | $17.99 |
|
| 81 |
-
| Margherita | Fresh mozzarella, tomatoes, basil | $15.99 |
|
| 82 |
-
| Hawaiian | Ham, pineapple | $14.99 |
|
| 83 |
-
|
| 84 |
-
---
|
| 85 |
-
|
| 86 |
-
### 5. **Additional Menu Items**
|
| 87 |
-
|
| 88 |
-
#### 5.1 **Side Orders**
|
| 89 |
-
|
| 90 |
-
| Item | Description | Price |
|
| 91 |
-
|--------------------|--------------------------------------|---------------|
|
| 92 |
-
| Garlic Breadsticks | Served with marinara dipping sauce | $5.99 |
|
| 93 |
-
| Chicken Wings | Buffalo, BBQ, or plain (10 pieces) | $9.99 |
|
| 94 |
-
| Mozzarella Sticks | Served with marinara (8 pieces) | $6.99 |
|
| 95 |
-
| Caesar Salad | Romaine, croutons, Caesar dressing | $7.99 |
|
| 96 |
-
|
| 97 |
-
#### 5.2 **Desserts**
|
| 98 |
-
|
| 99 |
-
| Item | Description | Price |
|
| 100 |
-
|--------------------|--------------------------------------|---------------|
|
| 101 |
-
| Chocolate Brownies | Chewy and rich (6 pieces) | $4.99 |
|
| 102 |
-
| Cinnamon Sticks | Dusted with cinnamon sugar | $5.99 |
|
| 103 |
-
|
| 104 |
-
---
|
| 105 |
-
|
| 106 |
-
### 6. **Drinks**
|
| 107 |
-
|
| 108 |
-
| Size | Price |
|
| 109 |
-
|--------------------|---------------|
|
| 110 |
-
| 20 oz Bottle | $1.99 |
|
| 111 |
-
| 2-Liter Bottle | $3.50 |
|
| 112 |
-
|
| 113 |
-
Available options: Coke, Diet Coke, Sprite, Root Beer, Lemonade.
|
| 114 |
-
|
| 115 |
-
---
|
| 116 |
-
|
| 117 |
-
### 7. **Delivery Fees & Minimum Order**
|
| 118 |
-
|
| 119 |
-
- **Delivery Fee**: $2.99
|
| 120 |
-
- **Minimum Order**: $12.00
|
| 121 |
-
|
| 122 |
-
*Note: Delivery fees and minimum order thresholds apply to all delivery orders within a 5-mile radius. Additional charges may apply for orders outside this zone.*
|
| 123 |
-
|
| 124 |
-
---
|
| 125 |
-
|
| 126 |
-
### 8. **Promotions & Discounts**
|
| 127 |
-
|
| 128 |
-
- **Monday Madness**: Buy one large pizza, get a second pizza for 50% off.
|
| 129 |
-
- **Student Discount**: 10% off with valid student ID (pickup only).
|
| 130 |
-
- **Family Deal**: 2 large pizzas, 1 side, and 2-liter soda for $29.99.
|
| 131 |
-
|
| 132 |
-
---
|
| 133 |
-
|
| 134 |
-
### 9. **Conclusion**
|
| 135 |
-
|
| 136 |
-
This pricing and menu structure is designed to offer a wide range of choices for our customers while maintaining competitive pricing and ensuring profitability. Please ensure all team members are familiar with the details in this document and implement it accordingly.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/uploads/molecules2.csv
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
name,smiles
|
| 2 |
-
ciprofloxacin,C1CNCCN1c(c2)c(F)cc3c2N(C4CC4)C=C(C3=O)C(=O)O
|
| 3 |
-
caffeine,CN1C=NC2=C1C(=O)N(C(=O)N2C)C
|
| 4 |
-
α-d-glucopyranose,C([C@@H]1[C@H]([C@@H]([C@H]([C@H](O1)O)O)O)O)O
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/uploads/plus-one-dataset.parquet
DELETED
|
Binary file (7.54 kB)
|
|
|
examples/word2vec.py
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
from lynxkite.core.ops import op
|
| 2 |
-
import pandas as pd
|
| 3 |
-
|
| 4 |
-
ENV = "LynxKite Graph Analytics"
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
@op(ENV, "Word2vec for the top 1000 words", slow=True)
|
| 8 |
-
def word2vec_1000():
|
| 9 |
-
import staticvectors # ty: ignore[unresolved-import]
|
| 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",
|
| 14 |
-
names=["word"],
|
| 15 |
-
)
|
| 16 |
-
df["embedding"] = model.embeddings(df.word.tolist()).tolist()
|
| 17 |
-
return df
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
@op(ENV, "Take first N")
|
| 21 |
-
def first_n(df: pd.DataFrame, *, n=10):
|
| 22 |
-
return df.head(n)
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
@op(ENV, "Sample N")
|
| 26 |
-
def sample_n(df: pd.DataFrame, *, n=10):
|
| 27 |
-
return df.sample(n)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lynxkite-app/.gitignore
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
/src/lynxkite_app/web_assets
|
| 2 |
-
!/src/lynxkite_app/web_assets/__init__.py
|
| 3 |
-
!/src/lynxkite_app/web_assets/assets/__init__.py
|
| 4 |
-
data/
|
| 5 |
-
!/web/tests/data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lynxkite-app/MANIFEST.in
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
graft web
|
| 2 |
-
prune web/node_modules
|
|
|
|
|
|
|
|
|
lynxkite-app/README.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 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 |
-
uv pip install -e .
|
| 16 |
-
cd ../examples && LYNXKITE_RELOAD=1 lynxkite
|
| 17 |
-
```
|
| 18 |
-
|
| 19 |
-
To run the frontend:
|
| 20 |
-
|
| 21 |
-
```bash
|
| 22 |
-
cd web
|
| 23 |
-
npm i
|
| 24 |
-
npm run dev
|
| 25 |
-
```
|
| 26 |
-
|
| 27 |
-
To update the frontend types with the backend types:
|
| 28 |
-
|
| 29 |
-
```bash
|
| 30 |
-
$ uv run pydantic2ts --module lynxkite_app.main --output ./web/src/apiTypes.ts --json2ts-cmd "npx json-schema-to-typescript"
|
| 31 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lynxkite-app/pyproject.toml
DELETED
|
@@ -1,60 +0,0 @@
|
|
| 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 |
-
"griffe>=1.7.3",
|
| 10 |
-
"joblib>=1.5.1",
|
| 11 |
-
"lynxkite-core",
|
| 12 |
-
"pycrdt-websocket>=0.16",
|
| 13 |
-
"pycrdt>=0.12.26",
|
| 14 |
-
"pydantic>=2.11.7",
|
| 15 |
-
"sse-starlette>=2.2.1",
|
| 16 |
-
"uvicorn>=0.35.0",
|
| 17 |
-
]
|
| 18 |
-
classifiers = ["Private :: Do Not Upload"]
|
| 19 |
-
|
| 20 |
-
[project.urls]
|
| 21 |
-
Homepage = "https://github.com/lynxkite/lynxkite-2000/"
|
| 22 |
-
|
| 23 |
-
[dependency-groups]
|
| 24 |
-
dev = [
|
| 25 |
-
"pydantic-to-typescript>=2.0.0",
|
| 26 |
-
"setuptools>=80.9.0",
|
| 27 |
-
]
|
| 28 |
-
|
| 29 |
-
[tool.uv.sources]
|
| 30 |
-
lynxkite-core = { workspace = true }
|
| 31 |
-
|
| 32 |
-
[build-system]
|
| 33 |
-
requires = ["setuptools", "wheel", "setuptools-scm"]
|
| 34 |
-
build-backend = "setuptools.build_meta"
|
| 35 |
-
|
| 36 |
-
[tool.setuptools.packages.find]
|
| 37 |
-
namespaces = true
|
| 38 |
-
where = ["src"]
|
| 39 |
-
|
| 40 |
-
[tool.setuptools.package-data]
|
| 41 |
-
"lynxkite_app.web_assets" = ["*"]
|
| 42 |
-
"lynxkite_app.web_assets.assets" = ["*"]
|
| 43 |
-
|
| 44 |
-
[tool.setuptools]
|
| 45 |
-
py-modules = ["build_frontend"]
|
| 46 |
-
include-package-data = true
|
| 47 |
-
|
| 48 |
-
[tool.setuptools.cmdclass]
|
| 49 |
-
build_py = "build_frontend.build_py"
|
| 50 |
-
|
| 51 |
-
[project.scripts]
|
| 52 |
-
lynxkite = "lynxkite_app.__main__:main"
|
| 53 |
-
|
| 54 |
-
[tool.deptry.package_module_name_map]
|
| 55 |
-
lynxkite-core = "lynxkite"
|
| 56 |
-
sse-starlette = "starlette"
|
| 57 |
-
|
| 58 |
-
[tool.deptry.per_rule_ignores]
|
| 59 |
-
DEP002 = ["pycrdt-websocket", "griffe"]
|
| 60 |
-
DEP004 = ["setuptools"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lynxkite-app/src/build_frontend.py
DELETED
|
@@ -1,26 +0,0 @@
|
|
| 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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lynxkite-app/src/lynxkite_app/__init__.py
DELETED
|
File without changes
|
lynxkite-app/src/lynxkite_app/__main__.py
DELETED
|
@@ -1,20 +0,0 @@
|
|
| 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(
|
| 10 |
-
"lynxkite_app.main:app",
|
| 11 |
-
host="0.0.0.0",
|
| 12 |
-
port=port,
|
| 13 |
-
reload=reload,
|
| 14 |
-
loop="asyncio",
|
| 15 |
-
proxy_headers=True,
|
| 16 |
-
)
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
if __name__ == "__main__":
|
| 20 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lynxkite-app/src/lynxkite_app/crdt.py
DELETED
|
@@ -1,342 +0,0 @@
|
|
| 1 |
-
"""CRDT is used to synchronize workspace state for backend and frontend(s)."""
|
| 2 |
-
|
| 3 |
-
import asyncio
|
| 4 |
-
import contextlib
|
| 5 |
-
import enum
|
| 6 |
-
import pathlib
|
| 7 |
-
import fastapi
|
| 8 |
-
import os.path
|
| 9 |
-
import pycrdt.websocket
|
| 10 |
-
import pycrdt.store.file
|
| 11 |
-
import uvicorn.protocols.utils
|
| 12 |
-
import builtins
|
| 13 |
-
from lynxkite.core import workspace, ops
|
| 14 |
-
|
| 15 |
-
router = fastapi.APIRouter()
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def ws_exception_handler(exception, log):
|
| 19 |
-
if isinstance(exception, builtins.ExceptionGroup):
|
| 20 |
-
for ex in exception.exceptions:
|
| 21 |
-
if not isinstance(ex, uvicorn.protocols.utils.ClientDisconnected):
|
| 22 |
-
log.exception(ex)
|
| 23 |
-
else:
|
| 24 |
-
log.exception(exception)
|
| 25 |
-
return True
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
class WorkspaceWebsocketServer(pycrdt.websocket.WebsocketServer):
|
| 29 |
-
async def init_room(self, name: str) -> pycrdt.websocket.YRoom:
|
| 30 |
-
"""Initialize a room for the workspace with the given name.
|
| 31 |
-
|
| 32 |
-
The workspace is loaded from ".crdt" if it exists there, or from a JSON file, or a new workspace is created.
|
| 33 |
-
"""
|
| 34 |
-
crdt_path = pathlib.Path(".crdt")
|
| 35 |
-
path = crdt_path / f"{name}.crdt"
|
| 36 |
-
assert path.is_relative_to(crdt_path), f"Path '{path}' is invalid"
|
| 37 |
-
ystore = pycrdt.store.file.FileYStore(path)
|
| 38 |
-
ydoc = pycrdt.Doc()
|
| 39 |
-
ydoc["workspace"] = ws = pycrdt.Map()
|
| 40 |
-
# Replay updates from the store.
|
| 41 |
-
try:
|
| 42 |
-
for update, timestamp in [(item[0], item[-1]) async for item in ystore.read()]:
|
| 43 |
-
ydoc.apply_update(update)
|
| 44 |
-
except pycrdt.store.YDocNotFound:
|
| 45 |
-
pass
|
| 46 |
-
if "nodes" not in ws:
|
| 47 |
-
ws["nodes"] = pycrdt.Array()
|
| 48 |
-
if "edges" not in ws:
|
| 49 |
-
ws["edges"] = pycrdt.Array()
|
| 50 |
-
if "env" not in ws:
|
| 51 |
-
ws["env"] = next(iter(ops.CATALOGS), "unset")
|
| 52 |
-
# We have two possible sources of truth for the workspaces, the YStore and the JSON files.
|
| 53 |
-
# In case we didn't find the workspace in the YStore, we try to load it from the JSON files.
|
| 54 |
-
try_to_load_workspace(ws, name)
|
| 55 |
-
ws_simple = workspace.Workspace.model_validate(ws.to_py())
|
| 56 |
-
clean_input(ws_simple)
|
| 57 |
-
# Set the last known version to the current state, so we don't trigger a change event.
|
| 58 |
-
last_known_versions[name] = ws_simple
|
| 59 |
-
room = pycrdt.websocket.YRoom(
|
| 60 |
-
ystore=ystore, ydoc=ydoc, exception_handler=ws_exception_handler
|
| 61 |
-
)
|
| 62 |
-
# We hang the YDoc pointer on the room, so it only gets garbage collected when the room does.
|
| 63 |
-
room.ws = ws # ty: ignore[unresolved-attribute]
|
| 64 |
-
|
| 65 |
-
def on_change(changes):
|
| 66 |
-
task = asyncio.create_task(workspace_changed(name, changes, ws))
|
| 67 |
-
# We have no way to await workspace_changed(). The best we can do is to
|
| 68 |
-
# dereference its result after it's done, so exceptions are logged normally.
|
| 69 |
-
task.add_done_callback(lambda t: t.result())
|
| 70 |
-
|
| 71 |
-
ws.observe_deep(on_change)
|
| 72 |
-
return room
|
| 73 |
-
|
| 74 |
-
async def get_room(self, name: str) -> pycrdt.websocket.YRoom:
|
| 75 |
-
"""Get a room by name.
|
| 76 |
-
|
| 77 |
-
This method overrides the parent get_room method. The original creates an empty room,
|
| 78 |
-
with no associated Ydoc. Instead, we want to initialize the the room with a Workspace
|
| 79 |
-
object.
|
| 80 |
-
"""
|
| 81 |
-
if name not in self.rooms:
|
| 82 |
-
self.rooms[name] = await self.init_room(name)
|
| 83 |
-
room = self.rooms[name]
|
| 84 |
-
await self.start_room(room)
|
| 85 |
-
return room
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
class CodeWebsocketServer(WorkspaceWebsocketServer):
|
| 89 |
-
async def init_room(self, name: str) -> pycrdt.websocket.YRoom:
|
| 90 |
-
"""Initialize a room for a text document with the given name."""
|
| 91 |
-
crdt_path = pathlib.Path(".crdt")
|
| 92 |
-
path = crdt_path / f"{name}.crdt"
|
| 93 |
-
assert path.is_relative_to(crdt_path), f"Path '{path}' is invalid"
|
| 94 |
-
ystore = pycrdt.store.file.FileYStore(path)
|
| 95 |
-
ydoc = pycrdt.Doc()
|
| 96 |
-
ydoc["text"] = text = pycrdt.Text()
|
| 97 |
-
# Replay updates from the store.
|
| 98 |
-
try:
|
| 99 |
-
for update, timestamp in [(item[0], item[-1]) async for item in ystore.read()]:
|
| 100 |
-
ydoc.apply_update(update)
|
| 101 |
-
except pycrdt.store.YDocNotFound:
|
| 102 |
-
pass
|
| 103 |
-
if len(text) == 0:
|
| 104 |
-
if os.path.exists(name):
|
| 105 |
-
with open(name, encoding="utf-8") as f:
|
| 106 |
-
text += f.read().replace("\r\n", "\n")
|
| 107 |
-
room = pycrdt.websocket.YRoom(
|
| 108 |
-
ystore=ystore, ydoc=ydoc, exception_handler=ws_exception_handler
|
| 109 |
-
)
|
| 110 |
-
# We hang the YDoc pointer on the room, so it only gets garbage collected when the room does.
|
| 111 |
-
room.text = text # ty: ignore[unresolved-attribute]
|
| 112 |
-
|
| 113 |
-
def on_change(changes):
|
| 114 |
-
asyncio.create_task(code_changed(name, changes, text))
|
| 115 |
-
|
| 116 |
-
text.observe(on_change)
|
| 117 |
-
return room
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
last_ws_input = None
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
def clean_input(ws_pyd):
|
| 124 |
-
"""Delete everything that we want to ignore for the purposes of change detection."""
|
| 125 |
-
for node in ws_pyd.nodes:
|
| 126 |
-
node.data.display = None
|
| 127 |
-
node.data.input_metadata = None
|
| 128 |
-
node.data.error = None
|
| 129 |
-
node.data.status = workspace.NodeStatus.done
|
| 130 |
-
for p in list(node.data.params):
|
| 131 |
-
if p.startswith("_"):
|
| 132 |
-
del node.data.params[p]
|
| 133 |
-
if node.data.op_id == "Comment":
|
| 134 |
-
node.data.params = {}
|
| 135 |
-
node.position.x = 0
|
| 136 |
-
node.position.y = 0
|
| 137 |
-
node.width = 0
|
| 138 |
-
node.height = 0
|
| 139 |
-
if node.model_extra:
|
| 140 |
-
for key in list(node.model_extra.keys()):
|
| 141 |
-
delattr(node, key)
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
def crdt_update(
|
| 145 |
-
crdt_obj: pycrdt.Map | pycrdt.Array,
|
| 146 |
-
python_obj: dict | list,
|
| 147 |
-
non_collaborative_fields: set[str] = set(),
|
| 148 |
-
):
|
| 149 |
-
"""Update a CRDT object to match a Python object.
|
| 150 |
-
|
| 151 |
-
The types between the CRDT object and the Python object must match. If the Python object
|
| 152 |
-
is a dict, the CRDT object must be a Map. If the Python object is a list, the CRDT object
|
| 153 |
-
must be an Array.
|
| 154 |
-
|
| 155 |
-
Args:
|
| 156 |
-
crdt_obj: The CRDT object, that will be updated to match the Python object.
|
| 157 |
-
python_obj: The Python object to update with.
|
| 158 |
-
non_collaborative_fields: List of fields to treat as a black box. Black boxes are
|
| 159 |
-
updated as a whole, instead of having a fine-grained data structure to edit
|
| 160 |
-
collaboratively. Useful for complex fields that contain auto-generated data or
|
| 161 |
-
metadata.
|
| 162 |
-
The default is an empty set.
|
| 163 |
-
|
| 164 |
-
Raises:
|
| 165 |
-
ValueError: If the Python object provided is not a dict or list.
|
| 166 |
-
"""
|
| 167 |
-
if isinstance(python_obj, dict):
|
| 168 |
-
assert isinstance(crdt_obj, pycrdt.Map), "CRDT object must be a Map for a dict input"
|
| 169 |
-
for key, value in python_obj.items():
|
| 170 |
-
if key in non_collaborative_fields:
|
| 171 |
-
crdt_obj[key] = value
|
| 172 |
-
elif isinstance(value, dict):
|
| 173 |
-
if crdt_obj.get(key) is None:
|
| 174 |
-
crdt_obj[key] = pycrdt.Map()
|
| 175 |
-
crdt_update(crdt_obj[key], value, non_collaborative_fields)
|
| 176 |
-
elif isinstance(value, list):
|
| 177 |
-
if crdt_obj.get(key) is None:
|
| 178 |
-
crdt_obj[key] = pycrdt.Array()
|
| 179 |
-
crdt_update(crdt_obj[key], value, non_collaborative_fields)
|
| 180 |
-
elif isinstance(value, enum.Enum):
|
| 181 |
-
crdt_obj[key] = str(value.value)
|
| 182 |
-
else:
|
| 183 |
-
crdt_obj[key] = value
|
| 184 |
-
elif isinstance(python_obj, list):
|
| 185 |
-
assert isinstance(crdt_obj, pycrdt.Array), "CRDT object must be an Array for a list input"
|
| 186 |
-
for i, value in enumerate(python_obj):
|
| 187 |
-
if isinstance(value, dict):
|
| 188 |
-
if i >= len(crdt_obj):
|
| 189 |
-
crdt_obj.append(pycrdt.Map())
|
| 190 |
-
crdt_update(crdt_obj[i], value, non_collaborative_fields)
|
| 191 |
-
elif isinstance(value, list):
|
| 192 |
-
if i >= len(crdt_obj):
|
| 193 |
-
crdt_obj.append(pycrdt.Array())
|
| 194 |
-
crdt_update(crdt_obj[i], value, non_collaborative_fields)
|
| 195 |
-
else:
|
| 196 |
-
if isinstance(value, enum.Enum):
|
| 197 |
-
value = str(value.value)
|
| 198 |
-
if i >= len(crdt_obj):
|
| 199 |
-
crdt_obj.append(value)
|
| 200 |
-
else:
|
| 201 |
-
crdt_obj[i] = value
|
| 202 |
-
else:
|
| 203 |
-
raise ValueError("Invalid type:", python_obj)
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
def try_to_load_workspace(ws: pycrdt.Map, name: str):
|
| 207 |
-
"""Load the workspace `name`, if it exists, and update the `ws` CRDT object to match its contents.
|
| 208 |
-
|
| 209 |
-
Args:
|
| 210 |
-
ws: CRDT object to udpate with the workspace contents.
|
| 211 |
-
name: Name of the workspace to load.
|
| 212 |
-
"""
|
| 213 |
-
if os.path.exists(name):
|
| 214 |
-
ws_pyd = workspace.Workspace.load(name)
|
| 215 |
-
crdt_update(
|
| 216 |
-
ws,
|
| 217 |
-
ws_pyd.model_dump(),
|
| 218 |
-
# We treat some fields as black boxes. They are not edited on the frontend.
|
| 219 |
-
non_collaborative_fields={"display", "input_metadata", "meta"},
|
| 220 |
-
)
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
last_known_versions = {}
|
| 224 |
-
delayed_executions = {}
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
async def workspace_changed(name: str, changes: list[pycrdt.MapEvent], ws_crdt: pycrdt.Map):
|
| 228 |
-
"""Callback to react to changes in the workspace.
|
| 229 |
-
|
| 230 |
-
Args:
|
| 231 |
-
name: Name of the workspace.
|
| 232 |
-
changes: Changes performed to the workspace.
|
| 233 |
-
ws_crdt: CRDT object representing the workspace.
|
| 234 |
-
"""
|
| 235 |
-
ws_pyd = workspace.Workspace.model_validate(ws_crdt.to_py())
|
| 236 |
-
# Do not trigger execution for superficial changes.
|
| 237 |
-
# This is a quick solution until we build proper caching.
|
| 238 |
-
ws_simple = ws_pyd.model_copy(deep=True)
|
| 239 |
-
clean_input(ws_simple)
|
| 240 |
-
if ws_simple == last_known_versions.get(name):
|
| 241 |
-
return
|
| 242 |
-
last_known_versions[name] = ws_simple
|
| 243 |
-
# Frontend changes that result from typing are delayed to avoid
|
| 244 |
-
# rerunning the workspace for every keystroke.
|
| 245 |
-
if name in delayed_executions:
|
| 246 |
-
delayed_executions[name].cancel()
|
| 247 |
-
delay = min(
|
| 248 |
-
getattr(change, "keys", {}).get("__execution_delay", {}).get("newValue", 0)
|
| 249 |
-
for change in changes
|
| 250 |
-
)
|
| 251 |
-
# Check if workspace is paused - if so, skip automatic execution
|
| 252 |
-
if getattr(ws_pyd, "paused", False):
|
| 253 |
-
print(f"Skipping automatic execution for {name} in {ws_pyd.env} - workspace is paused")
|
| 254 |
-
return
|
| 255 |
-
if delay:
|
| 256 |
-
task = asyncio.create_task(execute(name, ws_crdt, ws_pyd, delay))
|
| 257 |
-
delayed_executions[name] = task
|
| 258 |
-
else:
|
| 259 |
-
await execute(name, ws_crdt, ws_pyd)
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
async def execute(name: str, ws_crdt: pycrdt.Map, ws_pyd: workspace.Workspace, delay: int = 0):
|
| 263 |
-
"""Execute the workspace and update the CRDT object with the results.
|
| 264 |
-
|
| 265 |
-
Args:
|
| 266 |
-
name: Name of the workspace.
|
| 267 |
-
ws_crdt: CRDT object representing the workspace.
|
| 268 |
-
ws_pyd: Workspace object to execute.
|
| 269 |
-
delay: Wait time before executing the workspace. The default is 0.
|
| 270 |
-
"""
|
| 271 |
-
if delay:
|
| 272 |
-
try:
|
| 273 |
-
await asyncio.sleep(delay)
|
| 274 |
-
except asyncio.CancelledError:
|
| 275 |
-
return
|
| 276 |
-
print(f"Running {name} in {ws_pyd.env}...")
|
| 277 |
-
cwd = pathlib.Path()
|
| 278 |
-
path = cwd / name
|
| 279 |
-
assert path.is_relative_to(cwd), f"Path '{path}' is invalid"
|
| 280 |
-
# Save user changes before executing, in case the execution fails.
|
| 281 |
-
ws_pyd.save(path)
|
| 282 |
-
ops.load_user_scripts(name)
|
| 283 |
-
ws_pyd.connect_crdt(ws_crdt)
|
| 284 |
-
ws_pyd.update_metadata()
|
| 285 |
-
if not ws_pyd.has_executor():
|
| 286 |
-
return
|
| 287 |
-
with ws_crdt.doc.transaction():
|
| 288 |
-
for nc in ws_crdt["nodes"]:
|
| 289 |
-
nc["data"]["status"] = "planned"
|
| 290 |
-
ws_pyd.normalize()
|
| 291 |
-
await ws_pyd.execute()
|
| 292 |
-
ws_pyd.save(path)
|
| 293 |
-
print(f"Finished running {name} in {ws_pyd.env}.")
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
async def code_changed(name: str, changes: pycrdt.TextEvent, text: pycrdt.Text):
|
| 297 |
-
contents = str(text).strip() + "\n"
|
| 298 |
-
with open(name, "w", encoding="utf-8") as f:
|
| 299 |
-
f.write(contents)
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
ws_websocket_server: WorkspaceWebsocketServer
|
| 303 |
-
code_websocket_server: CodeWebsocketServer
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
def get_room(name):
|
| 307 |
-
return ws_websocket_server.get_room(name)
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
@contextlib.asynccontextmanager
|
| 311 |
-
async def lifespan(app):
|
| 312 |
-
global ws_websocket_server
|
| 313 |
-
global code_websocket_server
|
| 314 |
-
ws_websocket_server = WorkspaceWebsocketServer(auto_clean_rooms=False)
|
| 315 |
-
code_websocket_server = CodeWebsocketServer(auto_clean_rooms=False)
|
| 316 |
-
async with ws_websocket_server:
|
| 317 |
-
async with code_websocket_server:
|
| 318 |
-
yield
|
| 319 |
-
print("closing websocket server")
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
def delete_room(name: str):
|
| 323 |
-
if name in ws_websocket_server.rooms:
|
| 324 |
-
del ws_websocket_server.rooms[name]
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
def sanitize_path(path):
|
| 328 |
-
return os.path.relpath(os.path.normpath(os.path.join("/", path)), "/")
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
@router.websocket("/ws/crdt/{room_name:path}")
|
| 332 |
-
async def crdt_websocket(websocket: fastapi.WebSocket, room_name: str):
|
| 333 |
-
room_name = sanitize_path(room_name)
|
| 334 |
-
server = pycrdt.websocket.ASGIServer(ws_websocket_server)
|
| 335 |
-
await server({"path": room_name, "type": "websocket"}, websocket._receive, websocket._send)
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
@router.websocket("/ws/code/crdt/{room_name:path}")
|
| 339 |
-
async def code_crdt_websocket(websocket: fastapi.WebSocket, room_name: str):
|
| 340 |
-
room_name = sanitize_path(room_name)
|
| 341 |
-
server = pycrdt.websocket.ASGIServer(code_websocket_server)
|
| 342 |
-
await server({"path": room_name, "type": "websocket"}, websocket._receive, websocket._send)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lynxkite-app/src/lynxkite_app/main.py
DELETED
|
@@ -1,165 +0,0 @@
|
|
| 1 |
-
"""The FastAPI server for serving the LynxKite application."""
|
| 2 |
-
|
| 3 |
-
import shutil
|
| 4 |
-
import pydantic
|
| 5 |
-
import fastapi
|
| 6 |
-
import importlib
|
| 7 |
-
import joblib
|
| 8 |
-
import pathlib
|
| 9 |
-
import pkgutil
|
| 10 |
-
from fastapi.staticfiles import StaticFiles
|
| 11 |
-
from fastapi.middleware.gzip import GZipMiddleware
|
| 12 |
-
import starlette.exceptions
|
| 13 |
-
from lynxkite.core import ops
|
| 14 |
-
from lynxkite.core import workspace
|
| 15 |
-
from . import crdt
|
| 16 |
-
|
| 17 |
-
mem = joblib.Memory(".joblib-cache")
|
| 18 |
-
ops.CACHE_WRAPPER = mem.cache
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
def detect_plugins():
|
| 22 |
-
plugins = {}
|
| 23 |
-
for _, name, _ in pkgutil.iter_modules():
|
| 24 |
-
if name.startswith("lynxkite_") and name != "lynxkite_app":
|
| 25 |
-
print(f"Importing {name}")
|
| 26 |
-
plugins[name] = importlib.import_module(name)
|
| 27 |
-
if not plugins:
|
| 28 |
-
print("No LynxKite plugins found. Be sure to install some!")
|
| 29 |
-
return plugins
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
lynxkite_plugins = detect_plugins()
|
| 33 |
-
ops.save_catalogs("plugins loaded")
|
| 34 |
-
|
| 35 |
-
app = fastapi.FastAPI(lifespan=crdt.lifespan)
|
| 36 |
-
app.include_router(crdt.router)
|
| 37 |
-
app.add_middleware(GZipMiddleware)
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
def _get_ops(env: str):
|
| 41 |
-
catalog = ops.CATALOGS[env]
|
| 42 |
-
res = {op.name: op.model_dump() for op in catalog.values()}
|
| 43 |
-
res.setdefault("Comment", ops.COMMENT_OP.model_dump())
|
| 44 |
-
return res
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
@app.get("/api/catalog")
|
| 48 |
-
def get_catalog(workspace: str):
|
| 49 |
-
ops.load_user_scripts(workspace)
|
| 50 |
-
return {env: _get_ops(env) for env in ops.CATALOGS}
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
data_path = pathlib.Path()
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
@app.post("/api/delete")
|
| 57 |
-
async def delete_workspace(req: dict):
|
| 58 |
-
json_path: pathlib.Path = data_path / req["path"]
|
| 59 |
-
crdt_path: pathlib.Path = data_path / ".crdt" / f"{req['path']}.crdt"
|
| 60 |
-
assert json_path.is_relative_to(data_path), f"Path '{json_path}' is invalid"
|
| 61 |
-
json_path.unlink()
|
| 62 |
-
crdt_path.unlink()
|
| 63 |
-
crdt.delete_room(req["path"])
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
class DirectoryEntry(pydantic.BaseModel):
|
| 67 |
-
name: str
|
| 68 |
-
type: str
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
def _get_path_type(path: pathlib.Path) -> str:
|
| 72 |
-
if path.is_dir():
|
| 73 |
-
return "directory"
|
| 74 |
-
elif path.suffixes[-2:] == [".lynxkite", ".json"]:
|
| 75 |
-
return "workspace"
|
| 76 |
-
else:
|
| 77 |
-
return "file"
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
@app.get("/api/dir/list")
|
| 81 |
-
def list_dir(path: str):
|
| 82 |
-
path = data_path / path
|
| 83 |
-
assert path.is_relative_to(data_path), f"Path '{path}' is invalid"
|
| 84 |
-
return sorted(
|
| 85 |
-
[
|
| 86 |
-
DirectoryEntry(
|
| 87 |
-
name=str(p.relative_to(data_path)),
|
| 88 |
-
type=_get_path_type(p),
|
| 89 |
-
)
|
| 90 |
-
for p in path.iterdir()
|
| 91 |
-
if not p.name.startswith(".")
|
| 92 |
-
],
|
| 93 |
-
key=lambda x: (x.type != "directory", x.name.lower()),
|
| 94 |
-
)
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
@app.post("/api/dir/mkdir")
|
| 98 |
-
def make_dir(req: dict):
|
| 99 |
-
path = data_path / req["path"]
|
| 100 |
-
assert path.is_relative_to(data_path), f"Path '{path}' is invalid"
|
| 101 |
-
assert not path.exists(), f"{path} already exists"
|
| 102 |
-
path.mkdir()
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
@app.post("/api/dir/delete")
|
| 106 |
-
def delete_dir(req: dict):
|
| 107 |
-
path: pathlib.Path = data_path / req["path"]
|
| 108 |
-
assert all([path.is_relative_to(data_path), path.exists(), path.is_dir()]), (
|
| 109 |
-
f"Path '{path}' is invalid"
|
| 110 |
-
)
|
| 111 |
-
shutil.rmtree(path)
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
@app.get("/api/service/{module_path:path}")
|
| 115 |
-
async def service_get(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_get(req)
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
@app.post("/api/service/{module_path:path}")
|
| 122 |
-
async def service_post(req: fastapi.Request, module_path: str):
|
| 123 |
-
"""Executors can provide extra HTTP APIs through the /api/service endpoint."""
|
| 124 |
-
module = lynxkite_plugins[module_path.split("/")[0]]
|
| 125 |
-
return await module.api_service_post(req)
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
@app.post("/api/upload")
|
| 129 |
-
async def upload(req: fastapi.Request):
|
| 130 |
-
"""Receives file uploads and stores them in DATA_PATH."""
|
| 131 |
-
form = await req.form()
|
| 132 |
-
for file in form.values():
|
| 133 |
-
file_path = data_path / "uploads" / file.filename
|
| 134 |
-
assert file_path.is_relative_to(data_path), f"Path '{file_path}' is invalid"
|
| 135 |
-
with file_path.open("wb") as buffer:
|
| 136 |
-
shutil.copyfileobj(file.file, buffer)
|
| 137 |
-
return {"status": "ok"}
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
@app.post("/api/execute_workspace")
|
| 141 |
-
async def execute_workspace(name: str):
|
| 142 |
-
"""Trigger and await the execution of a workspace."""
|
| 143 |
-
room = await crdt.get_room(name)
|
| 144 |
-
ws_pyd = workspace.Workspace.model_validate(room.ws.to_py())
|
| 145 |
-
await crdt.execute(name, room.ws, ws_pyd)
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
class SPAStaticFiles(StaticFiles):
|
| 149 |
-
"""Route everything to index.html. https://stackoverflow.com/a/73552966/3318517"""
|
| 150 |
-
|
| 151 |
-
async def get_response(self, path: str, scope):
|
| 152 |
-
try:
|
| 153 |
-
return await super().get_response(path, scope)
|
| 154 |
-
except (
|
| 155 |
-
fastapi.HTTPException,
|
| 156 |
-
starlette.exceptions.HTTPException,
|
| 157 |
-
) as ex:
|
| 158 |
-
if ex.status_code == 404:
|
| 159 |
-
return await super().get_response(".", scope)
|
| 160 |
-
else:
|
| 161 |
-
raise ex
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
static_dir = SPAStaticFiles(packages=[("lynxkite_app", "web_assets")], html=True)
|
| 165 |
-
app.mount("/", static_dir, name="web_assets")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|