Spaces:
Running
Running
Merge pull request #89 from biggraph/darabos-upload
Browse files- Dockerfile +1 -1
- README.md +1 -1
- examples/AIMO +1 -1
- examples/Bio demo +1 -1
- examples/Graph RAG +1 -1
- examples/LynxScribe demo +4 -4
- examples/{aimo-examples.csv → uploads/aimo-examples.csv} +0 -0
- examples/{drug_target_data_sample.csv → uploads/drug_target_data_sample.csv} +0 -0
- examples/{example-pizza.md → uploads/example-pizza.md} +0 -0
- lynxkite-app/README.md +1 -1
- lynxkite-app/src/lynxkite_app/config.py +0 -8
- lynxkite-app/src/lynxkite_app/crdt.py +9 -8
- lynxkite-app/src/lynxkite_app/main.py +31 -16
- lynxkite-app/tests/test_main.py +7 -8
- lynxkite-app/web/package-lock.json +258 -2
- lynxkite-app/web/package.json +4 -3
- lynxkite-app/web/playwright.config.ts +1 -1
- lynxkite-app/web/src/index.css +16 -0
- lynxkite-app/web/src/workspace/Workspace.tsx +94 -25
- lynxkite-app/web/tests/basic.spec.ts +0 -3
- lynxkite-app/web/tests/examples.spec.ts +1 -8
- lynxkite-app/web/tests/lynxkite.ts +6 -12
- lynxkite-core/src/lynxkite/core/workspace.py +2 -1
- lynxkite-lynxscribe/src/lynxkite_lynxscribe/lynxscribe_ops.py +6 -9
Dockerfile
CHANGED
|
@@ -11,6 +11,6 @@ RUN uv venv && uv pip install \
|
|
| 11 |
-e lynxkite-graph-analytics \
|
| 12 |
-e lynxkite-bio \
|
| 13 |
-e lynxkite-pillow-example
|
| 14 |
-
|
| 15 |
ENV PORT=7860
|
| 16 |
CMD ["uv", "run", "lynxkite"]
|
|
|
|
| 11 |
-e lynxkite-graph-analytics \
|
| 12 |
-e lynxkite-bio \
|
| 13 |
-e lynxkite-pillow-example
|
| 14 |
+
WORKDIR $HOME/app/examples
|
| 15 |
ENV PORT=7860
|
| 16 |
CMD ["uv", "run", "lynxkite"]
|
README.md
CHANGED
|
@@ -41,7 +41,7 @@ uv pip install -e lynxkite-core/[dev] -e lynxkite-app/[dev] -e lynxkite-graph-an
|
|
| 41 |
This also builds the frontend, hopefully very quickly. To run it:
|
| 42 |
|
| 43 |
```bash
|
| 44 |
-
|
| 45 |
```
|
| 46 |
|
| 47 |
If you also want to make changes to the frontend with hot reloading:
|
|
|
|
| 41 |
This also builds the frontend, hopefully very quickly. To run it:
|
| 42 |
|
| 43 |
```bash
|
| 44 |
+
cd examples && LYNXKITE_RELOAD=1 lynxkite
|
| 45 |
```
|
| 46 |
|
| 47 |
If you also want to make changes to the frontend with hot reloading:
|
examples/AIMO
CHANGED
|
@@ -786,7 +786,7 @@
|
|
| 786 |
"data": {
|
| 787 |
"title": "Input CSV",
|
| 788 |
"params": {
|
| 789 |
-
"filename": "
|
| 790 |
"key": "problem"
|
| 791 |
},
|
| 792 |
"display": null,
|
|
|
|
| 786 |
"data": {
|
| 787 |
"title": "Input CSV",
|
| 788 |
"params": {
|
| 789 |
+
"filename": "uploads/aimo-examples.csv",
|
| 790 |
"key": "problem"
|
| 791 |
},
|
| 792 |
"display": null,
|
examples/Bio demo
CHANGED
|
@@ -7,7 +7,7 @@
|
|
| 7 |
"data": {
|
| 8 |
"title": "Import CSV",
|
| 9 |
"params": {
|
| 10 |
-
"filename": "
|
| 11 |
"separator": "<auto>",
|
| 12 |
"columns": "<from file>"
|
| 13 |
},
|
|
|
|
| 7 |
"data": {
|
| 8 |
"title": "Import CSV",
|
| 9 |
"params": {
|
| 10 |
+
"filename": "uploads/drug_target_data_sample.csv",
|
| 11 |
"separator": "<auto>",
|
| 12 |
"columns": "<from file>"
|
| 13 |
},
|
examples/Graph RAG
CHANGED
|
@@ -7,7 +7,7 @@
|
|
| 7 |
"data": {
|
| 8 |
"title": "Input document",
|
| 9 |
"params": {
|
| 10 |
-
"filename": "
|
| 11 |
},
|
| 12 |
"display": null,
|
| 13 |
"error": null,
|
|
|
|
| 7 |
"data": {
|
| 8 |
"title": "Input document",
|
| 9 |
"params": {
|
| 10 |
+
"filename": "uploads/example-pizza.md"
|
| 11 |
},
|
| 12 |
"display": null,
|
| 13 |
"error": null,
|
examples/LynxScribe demo
CHANGED
|
@@ -138,7 +138,7 @@
|
|
| 138 |
"data": {
|
| 139 |
"title": "Scenario selector",
|
| 140 |
"params": {
|
| 141 |
-
"scenario_file": "/
|
| 142 |
"node_types": "intent_cluster"
|
| 143 |
},
|
| 144 |
"display": null,
|
|
@@ -252,9 +252,9 @@
|
|
| 252 |
"data": {
|
| 253 |
"title": "Knowledge base",
|
| 254 |
"params": {
|
| 255 |
-
"template_cluster_path": "/
|
| 256 |
-
"edges_path": "/
|
| 257 |
-
"nodes_path": "/
|
| 258 |
},
|
| 259 |
"display": null,
|
| 260 |
"error": null,
|
|
|
|
| 138 |
"data": {
|
| 139 |
"title": "Scenario selector",
|
| 140 |
"params": {
|
| 141 |
+
"scenario_file": "uploads/chat_api/scenarios.yaml",
|
| 142 |
"node_types": "intent_cluster"
|
| 143 |
},
|
| 144 |
"display": null,
|
|
|
|
| 252 |
"data": {
|
| 253 |
"title": "Knowledge base",
|
| 254 |
"params": {
|
| 255 |
+
"template_cluster_path": "uploads/chat_api/data/lynx/tempclusters.pickle",
|
| 256 |
+
"edges_path": "uploads/chat_api/data/lynx/edges.pickle",
|
| 257 |
+
"nodes_path": "uploads/chat_api/data/lynx/nodes.pickle"
|
| 258 |
},
|
| 259 |
"display": null,
|
| 260 |
"error": null,
|
examples/{aimo-examples.csv → uploads/aimo-examples.csv}
RENAMED
|
File without changes
|
examples/{drug_target_data_sample.csv → uploads/drug_target_data_sample.csv}
RENAMED
|
File without changes
|
examples/{example-pizza.md → uploads/example-pizza.md}
RENAMED
|
File without changes
|
lynxkite-app/README.md
CHANGED
|
@@ -13,7 +13,7 @@ To run the backend:
|
|
| 13 |
|
| 14 |
```bash
|
| 15 |
uv pip install -e .
|
| 16 |
-
|
| 17 |
```
|
| 18 |
|
| 19 |
To run the frontend:
|
|
|
|
| 13 |
|
| 14 |
```bash
|
| 15 |
uv pip install -e .
|
| 16 |
+
cd ../examples && LYNXKITE_RELOAD=1 lynxkite
|
| 17 |
```
|
| 18 |
|
| 19 |
To run the frontend:
|
lynxkite-app/src/lynxkite_app/config.py
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
"""Some common configuration."""
|
| 2 |
-
|
| 3 |
-
import os
|
| 4 |
-
import pathlib
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
DATA_PATH = pathlib.Path(os.environ.get("LYNXKITE_DATA", "lynxkite_data"))
|
| 8 |
-
CRDT_PATH = pathlib.Path(os.environ.get("LYNXKITE_CRDT_DATA", "lynxkite_crdt_data"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lynxkite-app/src/lynxkite_app/crdt.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
| 3 |
import asyncio
|
| 4 |
import contextlib
|
| 5 |
import enum
|
|
|
|
| 6 |
import fastapi
|
| 7 |
import os.path
|
| 8 |
import pycrdt
|
|
@@ -11,7 +12,6 @@ import pycrdt_websocket.ystore
|
|
| 11 |
import uvicorn
|
| 12 |
import builtins
|
| 13 |
from lynxkite.core import workspace, ops
|
| 14 |
-
from . import config
|
| 15 |
|
| 16 |
router = fastapi.APIRouter()
|
| 17 |
|
|
@@ -32,8 +32,9 @@ class WebsocketServer(pycrdt_websocket.WebsocketServer):
|
|
| 32 |
|
| 33 |
The workspace is loaded from "crdt_data" if it exists there, or from "data", or a new workspace is created.
|
| 34 |
"""
|
| 35 |
-
|
| 36 |
-
|
|
|
|
| 37 |
ystore = pycrdt_websocket.ystore.FileYStore(path)
|
| 38 |
ydoc = pycrdt.Doc()
|
| 39 |
ydoc["workspace"] = ws = pycrdt.Map()
|
|
@@ -165,9 +166,8 @@ def try_to_load_workspace(ws: pycrdt.Map, name: str):
|
|
| 165 |
ws: CRDT object to udpate with the workspace contents.
|
| 166 |
name: Name of the workspace to load.
|
| 167 |
"""
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
ws_pyd = workspace.load(json_path)
|
| 171 |
# We treat the display field as a black box, since it is a large
|
| 172 |
# dictionary that is meant to change as a whole.
|
| 173 |
crdt_update(ws, ws_pyd.model_dump(), non_collaborative_fields={"display"})
|
|
@@ -225,8 +225,9 @@ async def execute(
|
|
| 225 |
except asyncio.CancelledError:
|
| 226 |
return
|
| 227 |
print(f"Running {name} in {ws_pyd.env}...")
|
| 228 |
-
|
| 229 |
-
|
|
|
|
| 230 |
# Save user changes before executing, in case the execution fails.
|
| 231 |
workspace.save(ws_pyd, path)
|
| 232 |
ws_pyd._crdt = ws_crdt
|
|
|
|
| 3 |
import asyncio
|
| 4 |
import contextlib
|
| 5 |
import enum
|
| 6 |
+
import pathlib
|
| 7 |
import fastapi
|
| 8 |
import os.path
|
| 9 |
import pycrdt
|
|
|
|
| 12 |
import uvicorn
|
| 13 |
import builtins
|
| 14 |
from lynxkite.core import workspace, ops
|
|
|
|
| 15 |
|
| 16 |
router = fastapi.APIRouter()
|
| 17 |
|
|
|
|
| 32 |
|
| 33 |
The workspace is loaded from "crdt_data" if it exists there, or from "data", or a new workspace is created.
|
| 34 |
"""
|
| 35 |
+
crdt_path = pathlib.Path(".crdt")
|
| 36 |
+
path = crdt_path / f"{name}.crdt"
|
| 37 |
+
assert path.is_relative_to(crdt_path)
|
| 38 |
ystore = pycrdt_websocket.ystore.FileYStore(path)
|
| 39 |
ydoc = pycrdt.Doc()
|
| 40 |
ydoc["workspace"] = ws = pycrdt.Map()
|
|
|
|
| 166 |
ws: CRDT object to udpate with the workspace contents.
|
| 167 |
name: Name of the workspace to load.
|
| 168 |
"""
|
| 169 |
+
if os.path.exists(name):
|
| 170 |
+
ws_pyd = workspace.load(name)
|
|
|
|
| 171 |
# We treat the display field as a black box, since it is a large
|
| 172 |
# dictionary that is meant to change as a whole.
|
| 173 |
crdt_update(ws, ws_pyd.model_dump(), non_collaborative_fields={"display"})
|
|
|
|
| 225 |
except asyncio.CancelledError:
|
| 226 |
return
|
| 227 |
print(f"Running {name} in {ws_pyd.env}...")
|
| 228 |
+
cwd = pathlib.Path()
|
| 229 |
+
path = cwd / name
|
| 230 |
+
assert path.is_relative_to(cwd), "Provided workspace path is invalid"
|
| 231 |
# Save user changes before executing, in case the execution fails.
|
| 232 |
workspace.save(ws_pyd, path)
|
| 233 |
ws_pyd._crdt = ws_crdt
|
lynxkite-app/src/lynxkite_app/main.py
CHANGED
|
@@ -11,7 +11,7 @@ from fastapi.middleware.gzip import GZipMiddleware
|
|
| 11 |
import starlette
|
| 12 |
from lynxkite.core import ops
|
| 13 |
from lynxkite.core import workspace
|
| 14 |
-
from . import crdt
|
| 15 |
|
| 16 |
|
| 17 |
def detect_plugins():
|
|
@@ -45,9 +45,12 @@ class SaveRequest(workspace.BaseConfig):
|
|
| 45 |
ws: workspace.Workspace
|
| 46 |
|
| 47 |
|
|
|
|
|
|
|
|
|
|
| 48 |
def save(req: SaveRequest):
|
| 49 |
-
path =
|
| 50 |
-
assert path.is_relative_to(
|
| 51 |
workspace.save(req.ws, path)
|
| 52 |
|
| 53 |
|
|
@@ -61,18 +64,17 @@ async def save_and_execute(req: SaveRequest):
|
|
| 61 |
|
| 62 |
@app.post("/api/delete")
|
| 63 |
async def delete_workspace(req: dict):
|
| 64 |
-
json_path: pathlib.Path =
|
| 65 |
-
crdt_path: pathlib.Path =
|
| 66 |
-
assert json_path.is_relative_to(
|
| 67 |
-
assert crdt_path.is_relative_to(config.CRDT_PATH)
|
| 68 |
json_path.unlink()
|
| 69 |
crdt_path.unlink()
|
| 70 |
|
| 71 |
|
| 72 |
@app.get("/api/load")
|
| 73 |
def load(path: str):
|
| 74 |
-
path =
|
| 75 |
-
assert path.is_relative_to(
|
| 76 |
if not path.exists():
|
| 77 |
return workspace.Workspace()
|
| 78 |
return workspace.load(path)
|
|
@@ -85,15 +87,16 @@ class DirectoryEntry(pydantic.BaseModel):
|
|
| 85 |
|
| 86 |
@app.get("/api/dir/list")
|
| 87 |
def list_dir(path: str):
|
| 88 |
-
path =
|
| 89 |
-
assert path.is_relative_to(
|
| 90 |
return sorted(
|
| 91 |
[
|
| 92 |
DirectoryEntry(
|
| 93 |
-
name=str(p.relative_to(
|
| 94 |
type="directory" if p.is_dir() else "workspace",
|
| 95 |
)
|
| 96 |
for p in path.iterdir()
|
|
|
|
| 97 |
],
|
| 98 |
key=lambda x: x.name,
|
| 99 |
)
|
|
@@ -101,16 +104,16 @@ def list_dir(path: str):
|
|
| 101 |
|
| 102 |
@app.post("/api/dir/mkdir")
|
| 103 |
def make_dir(req: dict):
|
| 104 |
-
path =
|
| 105 |
-
assert path.is_relative_to(
|
| 106 |
assert not path.exists(), f"{path} already exists"
|
| 107 |
path.mkdir()
|
| 108 |
|
| 109 |
|
| 110 |
@app.post("/api/dir/delete")
|
| 111 |
def delete_dir(req: dict):
|
| 112 |
-
path: pathlib.Path =
|
| 113 |
-
assert all([path.is_relative_to(
|
| 114 |
shutil.rmtree(path)
|
| 115 |
|
| 116 |
|
|
@@ -128,6 +131,18 @@ async def service_post(req: fastapi.Request, module_path: str):
|
|
| 128 |
return await module.api_service_post(req)
|
| 129 |
|
| 130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
class SPAStaticFiles(StaticFiles):
|
| 132 |
"""Route everything to index.html. https://stackoverflow.com/a/73552966/3318517"""
|
| 133 |
|
|
|
|
| 11 |
import starlette
|
| 12 |
from lynxkite.core import ops
|
| 13 |
from lynxkite.core import workspace
|
| 14 |
+
from . import crdt
|
| 15 |
|
| 16 |
|
| 17 |
def detect_plugins():
|
|
|
|
| 45 |
ws: workspace.Workspace
|
| 46 |
|
| 47 |
|
| 48 |
+
data_path = pathlib.Path()
|
| 49 |
+
|
| 50 |
+
|
| 51 |
def save(req: SaveRequest):
|
| 52 |
+
path = data_path / req.path
|
| 53 |
+
assert path.is_relative_to(data_path)
|
| 54 |
workspace.save(req.ws, path)
|
| 55 |
|
| 56 |
|
|
|
|
| 64 |
|
| 65 |
@app.post("/api/delete")
|
| 66 |
async def delete_workspace(req: dict):
|
| 67 |
+
json_path: pathlib.Path = data_path / req["path"]
|
| 68 |
+
crdt_path: pathlib.Path = data_path / ".crdt" / f"{req['path']}.crdt"
|
| 69 |
+
assert json_path.is_relative_to(data_path)
|
|
|
|
| 70 |
json_path.unlink()
|
| 71 |
crdt_path.unlink()
|
| 72 |
|
| 73 |
|
| 74 |
@app.get("/api/load")
|
| 75 |
def load(path: str):
|
| 76 |
+
path = data_path / path
|
| 77 |
+
assert path.is_relative_to(data_path)
|
| 78 |
if not path.exists():
|
| 79 |
return workspace.Workspace()
|
| 80 |
return workspace.load(path)
|
|
|
|
| 87 |
|
| 88 |
@app.get("/api/dir/list")
|
| 89 |
def list_dir(path: str):
|
| 90 |
+
path = data_path / path
|
| 91 |
+
assert path.is_relative_to(data_path)
|
| 92 |
return sorted(
|
| 93 |
[
|
| 94 |
DirectoryEntry(
|
| 95 |
+
name=str(p.relative_to(data_path)),
|
| 96 |
type="directory" if p.is_dir() else "workspace",
|
| 97 |
)
|
| 98 |
for p in path.iterdir()
|
| 99 |
+
if not p.name.startswith(".")
|
| 100 |
],
|
| 101 |
key=lambda x: x.name,
|
| 102 |
)
|
|
|
|
| 104 |
|
| 105 |
@app.post("/api/dir/mkdir")
|
| 106 |
def make_dir(req: dict):
|
| 107 |
+
path = data_path / req["path"]
|
| 108 |
+
assert path.is_relative_to(data_path)
|
| 109 |
assert not path.exists(), f"{path} already exists"
|
| 110 |
path.mkdir()
|
| 111 |
|
| 112 |
|
| 113 |
@app.post("/api/dir/delete")
|
| 114 |
def delete_dir(req: dict):
|
| 115 |
+
path: pathlib.Path = data_path / req["path"]
|
| 116 |
+
assert all([path.is_relative_to(data_path), path.exists(), path.is_dir()])
|
| 117 |
shutil.rmtree(path)
|
| 118 |
|
| 119 |
|
|
|
|
| 131 |
return await module.api_service_post(req)
|
| 132 |
|
| 133 |
|
| 134 |
+
@app.post("/api/upload")
|
| 135 |
+
async def upload(req: fastapi.Request):
|
| 136 |
+
"""Receives file uploads and stores them in DATA_PATH."""
|
| 137 |
+
form = await req.form()
|
| 138 |
+
for file in form.values():
|
| 139 |
+
file_path = data_path / "uploads" / file.filename
|
| 140 |
+
assert file_path.is_relative_to(data_path), "Invalid file path"
|
| 141 |
+
with file_path.open("wb") as buffer:
|
| 142 |
+
shutil.copyfileobj(file.file, buffer)
|
| 143 |
+
return {"status": "ok"}
|
| 144 |
+
|
| 145 |
+
|
| 146 |
class SPAStaticFiles(StaticFiles):
|
| 147 |
"""Route everything to index.html. https://stackoverflow.com/a/73552966/3318517"""
|
| 148 |
|
lynxkite-app/tests/test_main.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
|
|
| 1 |
import uuid
|
| 2 |
from fastapi.testclient import TestClient
|
| 3 |
from lynxkite_app.main import app, detect_plugins
|
| 4 |
-
from lynxkite_app.config import DATA_PATH
|
| 5 |
import os
|
| 6 |
|
| 7 |
|
|
@@ -56,10 +56,9 @@ def test_save_and_load():
|
|
| 56 |
|
| 57 |
|
| 58 |
def test_list_dir():
|
| 59 |
-
test_dir = str(uuid.uuid4())
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
test_file = test_dir_full_path / "test_file.txt"
|
| 63 |
test_file.touch()
|
| 64 |
response = client.get(f"/api/dir/list?path={str(test_dir)}")
|
| 65 |
assert response.status_code == 200
|
|
@@ -67,12 +66,12 @@ def test_list_dir():
|
|
| 67 |
assert response.json()[0]["name"] == f"{test_dir}/test_file.txt"
|
| 68 |
assert response.json()[0]["type"] == "workspace"
|
| 69 |
test_file.unlink()
|
| 70 |
-
|
| 71 |
|
| 72 |
|
| 73 |
def test_make_dir():
|
| 74 |
dir_name = str(uuid.uuid4())
|
| 75 |
response = client.post("/api/dir/mkdir", json={"path": dir_name})
|
| 76 |
assert response.status_code == 200
|
| 77 |
-
assert os.path.exists(
|
| 78 |
-
os.rmdir(
|
|
|
|
| 1 |
+
import pathlib
|
| 2 |
import uuid
|
| 3 |
from fastapi.testclient import TestClient
|
| 4 |
from lynxkite_app.main import app, detect_plugins
|
|
|
|
| 5 |
import os
|
| 6 |
|
| 7 |
|
|
|
|
| 56 |
|
| 57 |
|
| 58 |
def test_list_dir():
|
| 59 |
+
test_dir = pathlib.Path() / str(uuid.uuid4())
|
| 60 |
+
test_dir.mkdir(parents=True, exist_ok=True)
|
| 61 |
+
test_file = test_dir / "test_file.txt"
|
|
|
|
| 62 |
test_file.touch()
|
| 63 |
response = client.get(f"/api/dir/list?path={str(test_dir)}")
|
| 64 |
assert response.status_code == 200
|
|
|
|
| 66 |
assert response.json()[0]["name"] == f"{test_dir}/test_file.txt"
|
| 67 |
assert response.json()[0]["type"] == "workspace"
|
| 68 |
test_file.unlink()
|
| 69 |
+
test_dir.rmdir()
|
| 70 |
|
| 71 |
|
| 72 |
def test_make_dir():
|
| 73 |
dir_name = str(uuid.uuid4())
|
| 74 |
response = client.post("/api/dir/mkdir", json={"path": dir_name})
|
| 75 |
assert response.status_code == 200
|
| 76 |
+
assert os.path.exists(dir_name)
|
| 77 |
+
os.rmdir(dir_name)
|
lynxkite-app/web/package-lock.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
| 17 |
"@syncedstore/react": "^0.6.0",
|
| 18 |
"@types/node": "^22.10.1",
|
| 19 |
"@xyflow/react": "^12.3.5",
|
|
|
|
| 20 |
"daisyui": "^4.12.20",
|
| 21 |
"echarts": "^5.5.1",
|
| 22 |
"fuse.js": "^7.0.0",
|
|
@@ -2467,6 +2468,12 @@
|
|
| 2467 |
"license": "MIT",
|
| 2468 |
"optional": true
|
| 2469 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2470 |
"node_modules/autoprefixer": {
|
| 2471 |
"version": "10.4.20",
|
| 2472 |
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
|
@@ -2505,6 +2512,17 @@
|
|
| 2505 |
"postcss": "^8.1.0"
|
| 2506 |
}
|
| 2507 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2508 |
"node_modules/bail": {
|
| 2509 |
"version": "2.0.2",
|
| 2510 |
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
|
@@ -2637,6 +2655,19 @@
|
|
| 2637 |
"ieee754": "^1.1.13"
|
| 2638 |
}
|
| 2639 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2640 |
"node_modules/callsites": {
|
| 2641 |
"version": "3.1.0",
|
| 2642 |
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
|
@@ -2824,6 +2855,18 @@
|
|
| 2824 |
"dev": true,
|
| 2825 |
"license": "MIT"
|
| 2826 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2827 |
"node_modules/comma-separated-tokens": {
|
| 2828 |
"version": "2.0.3",
|
| 2829 |
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
|
|
@@ -3126,6 +3169,15 @@
|
|
| 3126 |
"node": ">=6"
|
| 3127 |
}
|
| 3128 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3129 |
"node_modules/dequal": {
|
| 3130 |
"version": "2.0.3",
|
| 3131 |
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
|
@@ -3172,6 +3224,20 @@
|
|
| 3172 |
"tslib": "^2.0.3"
|
| 3173 |
}
|
| 3174 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3175 |
"node_modules/eastasianwidth": {
|
| 3176 |
"version": "0.2.0",
|
| 3177 |
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
|
@@ -3252,6 +3318,51 @@
|
|
| 3252 |
"is-arrayish": "^0.2.1"
|
| 3253 |
}
|
| 3254 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3255 |
"node_modules/esbuild": {
|
| 3256 |
"version": "0.25.0",
|
| 3257 |
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
|
|
@@ -3639,6 +3750,26 @@
|
|
| 3639 |
"dev": true,
|
| 3640 |
"license": "ISC"
|
| 3641 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3642 |
"node_modules/foreground-child": {
|
| 3643 |
"version": "3.3.0",
|
| 3644 |
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
|
@@ -3656,6 +3787,21 @@
|
|
| 3656 |
"url": "https://github.com/sponsors/isaacs"
|
| 3657 |
}
|
| 3658 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3659 |
"node_modules/fraction.js": {
|
| 3660 |
"version": "4.3.7",
|
| 3661 |
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
|
@@ -3689,7 +3835,6 @@
|
|
| 3689 |
"version": "1.1.2",
|
| 3690 |
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
| 3691 |
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
| 3692 |
-
"dev": true,
|
| 3693 |
"license": "MIT",
|
| 3694 |
"funding": {
|
| 3695 |
"url": "https://github.com/sponsors/ljharb"
|
|
@@ -3713,6 +3858,43 @@
|
|
| 3713 |
"node": ">=6.9.0"
|
| 3714 |
}
|
| 3715 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3716 |
"node_modules/glob": {
|
| 3717 |
"version": "10.4.5",
|
| 3718 |
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
|
@@ -3785,6 +3967,18 @@
|
|
| 3785 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 3786 |
}
|
| 3787 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3788 |
"node_modules/graphemer": {
|
| 3789 |
"version": "1.4.0",
|
| 3790 |
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
|
@@ -3802,11 +3996,37 @@
|
|
| 3802 |
"node": ">=8"
|
| 3803 |
}
|
| 3804 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3805 |
"node_modules/hasown": {
|
| 3806 |
"version": "2.0.2",
|
| 3807 |
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
| 3808 |
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
| 3809 |
-
"dev": true,
|
| 3810 |
"license": "MIT",
|
| 3811 |
"dependencies": {
|
| 3812 |
"function-bind": "^1.1.2"
|
|
@@ -4523,6 +4743,15 @@
|
|
| 4523 |
"license": "MIT",
|
| 4524 |
"optional": true
|
| 4525 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4526 |
"node_modules/mdast-util-from-markdown": {
|
| 4527 |
"version": "2.0.2",
|
| 4528 |
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
|
|
@@ -5142,6 +5371,27 @@
|
|
| 5142 |
"node": ">=8.6"
|
| 5143 |
}
|
| 5144 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5145 |
"node_modules/minimatch": {
|
| 5146 |
"version": "3.1.2",
|
| 5147 |
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
|
@@ -5766,6 +6016,12 @@
|
|
| 5766 |
"url": "https://github.com/sponsors/wooorm"
|
| 5767 |
}
|
| 5768 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5769 |
"node_modules/prr": {
|
| 5770 |
"version": "1.0.1",
|
| 5771 |
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
|
|
|
| 17 |
"@syncedstore/react": "^0.6.0",
|
| 18 |
"@types/node": "^22.10.1",
|
| 19 |
"@xyflow/react": "^12.3.5",
|
| 20 |
+
"axios": "^1.8.2",
|
| 21 |
"daisyui": "^4.12.20",
|
| 22 |
"echarts": "^5.5.1",
|
| 23 |
"fuse.js": "^7.0.0",
|
|
|
|
| 2468 |
"license": "MIT",
|
| 2469 |
"optional": true
|
| 2470 |
},
|
| 2471 |
+
"node_modules/asynckit": {
|
| 2472 |
+
"version": "0.4.0",
|
| 2473 |
+
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
| 2474 |
+
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
| 2475 |
+
"license": "MIT"
|
| 2476 |
+
},
|
| 2477 |
"node_modules/autoprefixer": {
|
| 2478 |
"version": "10.4.20",
|
| 2479 |
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
|
|
|
| 2512 |
"postcss": "^8.1.0"
|
| 2513 |
}
|
| 2514 |
},
|
| 2515 |
+
"node_modules/axios": {
|
| 2516 |
+
"version": "1.8.2",
|
| 2517 |
+
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
|
| 2518 |
+
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
|
| 2519 |
+
"license": "MIT",
|
| 2520 |
+
"dependencies": {
|
| 2521 |
+
"follow-redirects": "^1.15.6",
|
| 2522 |
+
"form-data": "^4.0.0",
|
| 2523 |
+
"proxy-from-env": "^1.1.0"
|
| 2524 |
+
}
|
| 2525 |
+
},
|
| 2526 |
"node_modules/bail": {
|
| 2527 |
"version": "2.0.2",
|
| 2528 |
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
|
|
|
| 2655 |
"ieee754": "^1.1.13"
|
| 2656 |
}
|
| 2657 |
},
|
| 2658 |
+
"node_modules/call-bind-apply-helpers": {
|
| 2659 |
+
"version": "1.0.2",
|
| 2660 |
+
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
| 2661 |
+
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
| 2662 |
+
"license": "MIT",
|
| 2663 |
+
"dependencies": {
|
| 2664 |
+
"es-errors": "^1.3.0",
|
| 2665 |
+
"function-bind": "^1.1.2"
|
| 2666 |
+
},
|
| 2667 |
+
"engines": {
|
| 2668 |
+
"node": ">= 0.4"
|
| 2669 |
+
}
|
| 2670 |
+
},
|
| 2671 |
"node_modules/callsites": {
|
| 2672 |
"version": "3.1.0",
|
| 2673 |
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
|
|
|
| 2855 |
"dev": true,
|
| 2856 |
"license": "MIT"
|
| 2857 |
},
|
| 2858 |
+
"node_modules/combined-stream": {
|
| 2859 |
+
"version": "1.0.8",
|
| 2860 |
+
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
| 2861 |
+
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
| 2862 |
+
"license": "MIT",
|
| 2863 |
+
"dependencies": {
|
| 2864 |
+
"delayed-stream": "~1.0.0"
|
| 2865 |
+
},
|
| 2866 |
+
"engines": {
|
| 2867 |
+
"node": ">= 0.8"
|
| 2868 |
+
}
|
| 2869 |
+
},
|
| 2870 |
"node_modules/comma-separated-tokens": {
|
| 2871 |
"version": "2.0.3",
|
| 2872 |
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
|
|
|
|
| 3169 |
"node": ">=6"
|
| 3170 |
}
|
| 3171 |
},
|
| 3172 |
+
"node_modules/delayed-stream": {
|
| 3173 |
+
"version": "1.0.0",
|
| 3174 |
+
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
| 3175 |
+
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
| 3176 |
+
"license": "MIT",
|
| 3177 |
+
"engines": {
|
| 3178 |
+
"node": ">=0.4.0"
|
| 3179 |
+
}
|
| 3180 |
+
},
|
| 3181 |
"node_modules/dequal": {
|
| 3182 |
"version": "2.0.3",
|
| 3183 |
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
|
|
|
| 3224 |
"tslib": "^2.0.3"
|
| 3225 |
}
|
| 3226 |
},
|
| 3227 |
+
"node_modules/dunder-proto": {
|
| 3228 |
+
"version": "1.0.1",
|
| 3229 |
+
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
| 3230 |
+
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
| 3231 |
+
"license": "MIT",
|
| 3232 |
+
"dependencies": {
|
| 3233 |
+
"call-bind-apply-helpers": "^1.0.1",
|
| 3234 |
+
"es-errors": "^1.3.0",
|
| 3235 |
+
"gopd": "^1.2.0"
|
| 3236 |
+
},
|
| 3237 |
+
"engines": {
|
| 3238 |
+
"node": ">= 0.4"
|
| 3239 |
+
}
|
| 3240 |
+
},
|
| 3241 |
"node_modules/eastasianwidth": {
|
| 3242 |
"version": "0.2.0",
|
| 3243 |
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
|
|
|
| 3318 |
"is-arrayish": "^0.2.1"
|
| 3319 |
}
|
| 3320 |
},
|
| 3321 |
+
"node_modules/es-define-property": {
|
| 3322 |
+
"version": "1.0.1",
|
| 3323 |
+
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
| 3324 |
+
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
| 3325 |
+
"license": "MIT",
|
| 3326 |
+
"engines": {
|
| 3327 |
+
"node": ">= 0.4"
|
| 3328 |
+
}
|
| 3329 |
+
},
|
| 3330 |
+
"node_modules/es-errors": {
|
| 3331 |
+
"version": "1.3.0",
|
| 3332 |
+
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
| 3333 |
+
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
| 3334 |
+
"license": "MIT",
|
| 3335 |
+
"engines": {
|
| 3336 |
+
"node": ">= 0.4"
|
| 3337 |
+
}
|
| 3338 |
+
},
|
| 3339 |
+
"node_modules/es-object-atoms": {
|
| 3340 |
+
"version": "1.1.1",
|
| 3341 |
+
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
| 3342 |
+
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
| 3343 |
+
"license": "MIT",
|
| 3344 |
+
"dependencies": {
|
| 3345 |
+
"es-errors": "^1.3.0"
|
| 3346 |
+
},
|
| 3347 |
+
"engines": {
|
| 3348 |
+
"node": ">= 0.4"
|
| 3349 |
+
}
|
| 3350 |
+
},
|
| 3351 |
+
"node_modules/es-set-tostringtag": {
|
| 3352 |
+
"version": "2.1.0",
|
| 3353 |
+
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
| 3354 |
+
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
| 3355 |
+
"license": "MIT",
|
| 3356 |
+
"dependencies": {
|
| 3357 |
+
"es-errors": "^1.3.0",
|
| 3358 |
+
"get-intrinsic": "^1.2.6",
|
| 3359 |
+
"has-tostringtag": "^1.0.2",
|
| 3360 |
+
"hasown": "^2.0.2"
|
| 3361 |
+
},
|
| 3362 |
+
"engines": {
|
| 3363 |
+
"node": ">= 0.4"
|
| 3364 |
+
}
|
| 3365 |
+
},
|
| 3366 |
"node_modules/esbuild": {
|
| 3367 |
"version": "0.25.0",
|
| 3368 |
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
|
|
|
|
| 3750 |
"dev": true,
|
| 3751 |
"license": "ISC"
|
| 3752 |
},
|
| 3753 |
+
"node_modules/follow-redirects": {
|
| 3754 |
+
"version": "1.15.9",
|
| 3755 |
+
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
| 3756 |
+
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
| 3757 |
+
"funding": [
|
| 3758 |
+
{
|
| 3759 |
+
"type": "individual",
|
| 3760 |
+
"url": "https://github.com/sponsors/RubenVerborgh"
|
| 3761 |
+
}
|
| 3762 |
+
],
|
| 3763 |
+
"license": "MIT",
|
| 3764 |
+
"engines": {
|
| 3765 |
+
"node": ">=4.0"
|
| 3766 |
+
},
|
| 3767 |
+
"peerDependenciesMeta": {
|
| 3768 |
+
"debug": {
|
| 3769 |
+
"optional": true
|
| 3770 |
+
}
|
| 3771 |
+
}
|
| 3772 |
+
},
|
| 3773 |
"node_modules/foreground-child": {
|
| 3774 |
"version": "3.3.0",
|
| 3775 |
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
|
|
|
| 3787 |
"url": "https://github.com/sponsors/isaacs"
|
| 3788 |
}
|
| 3789 |
},
|
| 3790 |
+
"node_modules/form-data": {
|
| 3791 |
+
"version": "4.0.2",
|
| 3792 |
+
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
| 3793 |
+
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
| 3794 |
+
"license": "MIT",
|
| 3795 |
+
"dependencies": {
|
| 3796 |
+
"asynckit": "^0.4.0",
|
| 3797 |
+
"combined-stream": "^1.0.8",
|
| 3798 |
+
"es-set-tostringtag": "^2.1.0",
|
| 3799 |
+
"mime-types": "^2.1.12"
|
| 3800 |
+
},
|
| 3801 |
+
"engines": {
|
| 3802 |
+
"node": ">= 6"
|
| 3803 |
+
}
|
| 3804 |
+
},
|
| 3805 |
"node_modules/fraction.js": {
|
| 3806 |
"version": "4.3.7",
|
| 3807 |
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
|
|
|
| 3835 |
"version": "1.1.2",
|
| 3836 |
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
| 3837 |
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
|
|
|
| 3838 |
"license": "MIT",
|
| 3839 |
"funding": {
|
| 3840 |
"url": "https://github.com/sponsors/ljharb"
|
|
|
|
| 3858 |
"node": ">=6.9.0"
|
| 3859 |
}
|
| 3860 |
},
|
| 3861 |
+
"node_modules/get-intrinsic": {
|
| 3862 |
+
"version": "1.3.0",
|
| 3863 |
+
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
| 3864 |
+
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
| 3865 |
+
"license": "MIT",
|
| 3866 |
+
"dependencies": {
|
| 3867 |
+
"call-bind-apply-helpers": "^1.0.2",
|
| 3868 |
+
"es-define-property": "^1.0.1",
|
| 3869 |
+
"es-errors": "^1.3.0",
|
| 3870 |
+
"es-object-atoms": "^1.1.1",
|
| 3871 |
+
"function-bind": "^1.1.2",
|
| 3872 |
+
"get-proto": "^1.0.1",
|
| 3873 |
+
"gopd": "^1.2.0",
|
| 3874 |
+
"has-symbols": "^1.1.0",
|
| 3875 |
+
"hasown": "^2.0.2",
|
| 3876 |
+
"math-intrinsics": "^1.1.0"
|
| 3877 |
+
},
|
| 3878 |
+
"engines": {
|
| 3879 |
+
"node": ">= 0.4"
|
| 3880 |
+
},
|
| 3881 |
+
"funding": {
|
| 3882 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 3883 |
+
}
|
| 3884 |
+
},
|
| 3885 |
+
"node_modules/get-proto": {
|
| 3886 |
+
"version": "1.0.1",
|
| 3887 |
+
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
| 3888 |
+
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
| 3889 |
+
"license": "MIT",
|
| 3890 |
+
"dependencies": {
|
| 3891 |
+
"dunder-proto": "^1.0.1",
|
| 3892 |
+
"es-object-atoms": "^1.0.0"
|
| 3893 |
+
},
|
| 3894 |
+
"engines": {
|
| 3895 |
+
"node": ">= 0.4"
|
| 3896 |
+
}
|
| 3897 |
+
},
|
| 3898 |
"node_modules/glob": {
|
| 3899 |
"version": "10.4.5",
|
| 3900 |
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
|
|
|
| 3967 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 3968 |
}
|
| 3969 |
},
|
| 3970 |
+
"node_modules/gopd": {
|
| 3971 |
+
"version": "1.2.0",
|
| 3972 |
+
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
| 3973 |
+
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
| 3974 |
+
"license": "MIT",
|
| 3975 |
+
"engines": {
|
| 3976 |
+
"node": ">= 0.4"
|
| 3977 |
+
},
|
| 3978 |
+
"funding": {
|
| 3979 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 3980 |
+
}
|
| 3981 |
+
},
|
| 3982 |
"node_modules/graphemer": {
|
| 3983 |
"version": "1.4.0",
|
| 3984 |
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
|
|
|
| 3996 |
"node": ">=8"
|
| 3997 |
}
|
| 3998 |
},
|
| 3999 |
+
"node_modules/has-symbols": {
|
| 4000 |
+
"version": "1.1.0",
|
| 4001 |
+
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
| 4002 |
+
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
| 4003 |
+
"license": "MIT",
|
| 4004 |
+
"engines": {
|
| 4005 |
+
"node": ">= 0.4"
|
| 4006 |
+
},
|
| 4007 |
+
"funding": {
|
| 4008 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 4009 |
+
}
|
| 4010 |
+
},
|
| 4011 |
+
"node_modules/has-tostringtag": {
|
| 4012 |
+
"version": "1.0.2",
|
| 4013 |
+
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
| 4014 |
+
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
| 4015 |
+
"license": "MIT",
|
| 4016 |
+
"dependencies": {
|
| 4017 |
+
"has-symbols": "^1.0.3"
|
| 4018 |
+
},
|
| 4019 |
+
"engines": {
|
| 4020 |
+
"node": ">= 0.4"
|
| 4021 |
+
},
|
| 4022 |
+
"funding": {
|
| 4023 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 4024 |
+
}
|
| 4025 |
+
},
|
| 4026 |
"node_modules/hasown": {
|
| 4027 |
"version": "2.0.2",
|
| 4028 |
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
| 4029 |
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
|
|
|
| 4030 |
"license": "MIT",
|
| 4031 |
"dependencies": {
|
| 4032 |
"function-bind": "^1.1.2"
|
|
|
|
| 4743 |
"license": "MIT",
|
| 4744 |
"optional": true
|
| 4745 |
},
|
| 4746 |
+
"node_modules/math-intrinsics": {
|
| 4747 |
+
"version": "1.1.0",
|
| 4748 |
+
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
| 4749 |
+
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
| 4750 |
+
"license": "MIT",
|
| 4751 |
+
"engines": {
|
| 4752 |
+
"node": ">= 0.4"
|
| 4753 |
+
}
|
| 4754 |
+
},
|
| 4755 |
"node_modules/mdast-util-from-markdown": {
|
| 4756 |
"version": "2.0.2",
|
| 4757 |
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
|
|
|
|
| 5371 |
"node": ">=8.6"
|
| 5372 |
}
|
| 5373 |
},
|
| 5374 |
+
"node_modules/mime-db": {
|
| 5375 |
+
"version": "1.52.0",
|
| 5376 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
| 5377 |
+
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
| 5378 |
+
"license": "MIT",
|
| 5379 |
+
"engines": {
|
| 5380 |
+
"node": ">= 0.6"
|
| 5381 |
+
}
|
| 5382 |
+
},
|
| 5383 |
+
"node_modules/mime-types": {
|
| 5384 |
+
"version": "2.1.35",
|
| 5385 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
| 5386 |
+
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
| 5387 |
+
"license": "MIT",
|
| 5388 |
+
"dependencies": {
|
| 5389 |
+
"mime-db": "1.52.0"
|
| 5390 |
+
},
|
| 5391 |
+
"engines": {
|
| 5392 |
+
"node": ">= 0.6"
|
| 5393 |
+
}
|
| 5394 |
+
},
|
| 5395 |
"node_modules/minimatch": {
|
| 5396 |
"version": "3.1.2",
|
| 5397 |
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
|
|
|
| 6016 |
"url": "https://github.com/sponsors/wooorm"
|
| 6017 |
}
|
| 6018 |
},
|
| 6019 |
+
"node_modules/proxy-from-env": {
|
| 6020 |
+
"version": "1.1.0",
|
| 6021 |
+
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
| 6022 |
+
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
| 6023 |
+
"license": "MIT"
|
| 6024 |
+
},
|
| 6025 |
"node_modules/prr": {
|
| 6026 |
"version": "1.0.1",
|
| 6027 |
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
lynxkite-app/web/package.json
CHANGED
|
@@ -20,6 +20,7 @@
|
|
| 20 |
"@syncedstore/react": "^0.6.0",
|
| 21 |
"@types/node": "^22.10.1",
|
| 22 |
"@xyflow/react": "^12.3.5",
|
|
|
|
| 23 |
"daisyui": "^4.12.20",
|
| 24 |
"echarts": "^5.5.1",
|
| 25 |
"fuse.js": "^7.0.0",
|
|
@@ -35,6 +36,8 @@
|
|
| 35 |
},
|
| 36 |
"devDependencies": {
|
| 37 |
"@eslint/js": "^9.15.0",
|
|
|
|
|
|
|
| 38 |
"@types/react": "^18.3.14",
|
| 39 |
"@types/react-dom": "^18.3.2",
|
| 40 |
"@vitejs/plugin-react-swc": "^3.5.0",
|
|
@@ -47,9 +50,7 @@
|
|
| 47 |
"tailwindcss": "^3.4.16",
|
| 48 |
"typescript": "~5.6.2",
|
| 49 |
"typescript-eslint": "^8.15.0",
|
| 50 |
-
"vite": "^6.2.0"
|
| 51 |
-
"@playwright/test": "^1.50.1",
|
| 52 |
-
"@types/node": "^22.13.1"
|
| 53 |
},
|
| 54 |
"optionalDependencies": {
|
| 55 |
"@rollup/rollup-linux-x64-gnu": "^4.28.1"
|
|
|
|
| 20 |
"@syncedstore/react": "^0.6.0",
|
| 21 |
"@types/node": "^22.10.1",
|
| 22 |
"@xyflow/react": "^12.3.5",
|
| 23 |
+
"axios": "^1.8.2",
|
| 24 |
"daisyui": "^4.12.20",
|
| 25 |
"echarts": "^5.5.1",
|
| 26 |
"fuse.js": "^7.0.0",
|
|
|
|
| 36 |
},
|
| 37 |
"devDependencies": {
|
| 38 |
"@eslint/js": "^9.15.0",
|
| 39 |
+
"@playwright/test": "^1.50.1",
|
| 40 |
+
"@types/node": "^22.13.1",
|
| 41 |
"@types/react": "^18.3.14",
|
| 42 |
"@types/react-dom": "^18.3.2",
|
| 43 |
"@vitejs/plugin-react-swc": "^3.5.0",
|
|
|
|
| 50 |
"tailwindcss": "^3.4.16",
|
| 51 |
"typescript": "~5.6.2",
|
| 52 |
"typescript-eslint": "^8.15.0",
|
| 53 |
+
"vite": "^6.2.0"
|
|
|
|
|
|
|
| 54 |
},
|
| 55 |
"optionalDependencies": {
|
| 56 |
"@rollup/rollup-linux-x64-gnu": "^4.28.1"
|
lynxkite-app/web/playwright.config.ts
CHANGED
|
@@ -23,7 +23,7 @@ export default defineConfig({
|
|
| 23 |
},
|
| 24 |
],
|
| 25 |
webServer: {
|
| 26 |
-
command: "cd
|
| 27 |
url: "http://127.0.0.1:8000",
|
| 28 |
reuseExistingServer: false,
|
| 29 |
},
|
|
|
|
| 23 |
},
|
| 24 |
],
|
| 25 |
webServer: {
|
| 26 |
+
command: "cd ../../examples && lynxkite",
|
| 27 |
url: "http://127.0.0.1:8000",
|
| 28 |
reuseExistingServer: false,
|
| 29 |
},
|
lynxkite-app/web/src/index.css
CHANGED
|
@@ -240,6 +240,22 @@ body {
|
|
| 240 |
background: transparent;
|
| 241 |
color: #39bcf3;
|
| 242 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
}
|
| 244 |
|
| 245 |
.params-expander {
|
|
|
|
| 240 |
background: transparent;
|
| 241 |
color: #39bcf3;
|
| 242 |
}
|
| 243 |
+
|
| 244 |
+
.workspace-message {
|
| 245 |
+
position: absolute;
|
| 246 |
+
left: 50%;
|
| 247 |
+
bottom: 20px;
|
| 248 |
+
transform: translateX(-50%);
|
| 249 |
+
box-shadow: 0 5px 50px 0px #8008;
|
| 250 |
+
padding: 10px 40px 10px 20px;
|
| 251 |
+
border-radius: 5px;
|
| 252 |
+
|
| 253 |
+
.close {
|
| 254 |
+
position: absolute;
|
| 255 |
+
right: 10px;
|
| 256 |
+
cursor: pointer;
|
| 257 |
+
}
|
| 258 |
+
}
|
| 259 |
}
|
| 260 |
|
| 261 |
.params-expander {
|
lynxkite-app/web/src/workspace/Workspace.tsx
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
| 14 |
useReactFlow,
|
| 15 |
useUpdateNodeInternals,
|
| 16 |
} from "@xyflow/react";
|
|
|
|
| 17 |
import {
|
| 18 |
type MouseEvent,
|
| 19 |
useCallback,
|
|
@@ -62,6 +63,7 @@ function LynxKiteFlow() {
|
|
| 62 |
const [edges, setEdges] = useState([] as Edge[]);
|
| 63 |
const { path } = useParams();
|
| 64 |
const [state, setState] = useState({ workspace: {} as Workspace });
|
|
|
|
| 65 |
useEffect(() => {
|
| 66 |
const state = syncedStore({ workspace: {} as Workspace });
|
| 67 |
setState(state);
|
|
@@ -80,7 +82,9 @@ function LynxKiteFlow() {
|
|
| 80 |
if (!state.workspace.nodes) return;
|
| 81 |
if (!state.workspace.edges) return;
|
| 82 |
for (const n of state.workspace.nodes) {
|
| 83 |
-
n.dragHandle
|
|
|
|
|
|
|
| 84 |
}
|
| 85 |
setNodes([...state.workspace.nodes] as Node[]);
|
| 86 |
setEdges([...state.workspace.edges] as Edge[]);
|
|
@@ -234,33 +238,44 @@ function LynxKiteFlow() {
|
|
| 234 |
},
|
| 235 |
[catalog, state, nodeSearchSettings, suppressSearchUntil, closeNodeSearch],
|
| 236 |
);
|
| 237 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
(meta: OpsOp) => {
|
| 239 |
-
const node
|
| 240 |
-
type: meta.type,
|
| 241 |
-
data: {
|
| 242 |
-
meta: meta,
|
| 243 |
-
title: meta.name,
|
| 244 |
-
params: Object.fromEntries(
|
| 245 |
-
Object.values(meta.params).map((p) => [p.name, p.default]),
|
| 246 |
-
),
|
| 247 |
-
},
|
| 248 |
-
};
|
| 249 |
const nss = nodeSearchSettings!;
|
| 250 |
node.position = reactFlow.screenToFlowPosition({
|
| 251 |
x: nss.pos.x,
|
| 252 |
y: nss.pos.y,
|
| 253 |
});
|
| 254 |
-
|
| 255 |
-
let i = 1;
|
| 256 |
-
node.id = `${title} ${i}`;
|
| 257 |
-
const wnodes = state.workspace.nodes!;
|
| 258 |
-
while (wnodes.find((x) => x.id === node.id)) {
|
| 259 |
-
i += 1;
|
| 260 |
-
node.id = `${title} ${i}`;
|
| 261 |
-
}
|
| 262 |
-
wnodes.push(node as WorkspaceNode);
|
| 263 |
-
setNodes([...nodes, node as WorkspaceNode]);
|
| 264 |
closeNodeSearch();
|
| 265 |
},
|
| 266 |
[nodeSearchSettings, state, reactFlow, nodes, closeNodeSearch],
|
|
@@ -282,6 +297,48 @@ function LynxKiteFlow() {
|
|
| 282 |
[state],
|
| 283 |
);
|
| 284 |
const parentDir = path!.split("/").slice(0, -1).join("/");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
return (
|
| 286 |
<div className="workspace">
|
| 287 |
<div className="top-bar bg-neutral">
|
|
@@ -308,7 +365,11 @@ function LynxKiteFlow() {
|
|
| 308 |
</a>
|
| 309 |
</div>
|
| 310 |
</div>
|
| 311 |
-
<div
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
<LynxKiteState.Provider value={state}>
|
| 313 |
<ReactFlow
|
| 314 |
nodes={nodes}
|
|
@@ -320,7 +381,7 @@ function LynxKiteFlow() {
|
|
| 320 |
onPaneClick={toggleNodeSearch}
|
| 321 |
onConnect={onConnect}
|
| 322 |
proOptions={{ hideAttribution: true }}
|
| 323 |
-
maxZoom={
|
| 324 |
minZoom={0.3}
|
| 325 |
defaultEdgeOptions={{
|
| 326 |
markerEnd: {
|
|
@@ -342,11 +403,19 @@ function LynxKiteFlow() {
|
|
| 342 |
pos={nodeSearchSettings.pos}
|
| 343 |
boxes={nodeSearchSettings.boxes}
|
| 344 |
onCancel={closeNodeSearch}
|
| 345 |
-
onAdd={
|
| 346 |
/>
|
| 347 |
)}
|
| 348 |
</ReactFlow>
|
| 349 |
</LynxKiteState.Provider>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 350 |
</div>
|
| 351 |
</div>
|
| 352 |
);
|
|
|
|
| 14 |
useReactFlow,
|
| 15 |
useUpdateNodeInternals,
|
| 16 |
} from "@xyflow/react";
|
| 17 |
+
import axios from "axios";
|
| 18 |
import {
|
| 19 |
type MouseEvent,
|
| 20 |
useCallback,
|
|
|
|
| 63 |
const [edges, setEdges] = useState([] as Edge[]);
|
| 64 |
const { path } = useParams();
|
| 65 |
const [state, setState] = useState({ workspace: {} as Workspace });
|
| 66 |
+
const [message, setMessage] = useState(null as string | null);
|
| 67 |
useEffect(() => {
|
| 68 |
const state = syncedStore({ workspace: {} as Workspace });
|
| 69 |
setState(state);
|
|
|
|
| 82 |
if (!state.workspace.nodes) return;
|
| 83 |
if (!state.workspace.edges) return;
|
| 84 |
for (const n of state.workspace.nodes) {
|
| 85 |
+
if (n.dragHandle !== ".bg-primary") {
|
| 86 |
+
n.dragHandle = ".bg-primary";
|
| 87 |
+
}
|
| 88 |
}
|
| 89 |
setNodes([...state.workspace.nodes] as Node[]);
|
| 90 |
setEdges([...state.workspace.edges] as Edge[]);
|
|
|
|
| 238 |
},
|
| 239 |
[catalog, state, nodeSearchSettings, suppressSearchUntil, closeNodeSearch],
|
| 240 |
);
|
| 241 |
+
function addNode(
|
| 242 |
+
node: Partial<WorkspaceNode>,
|
| 243 |
+
state: { workspace: Workspace },
|
| 244 |
+
nodes: Node[],
|
| 245 |
+
) {
|
| 246 |
+
const title = node.data?.title;
|
| 247 |
+
let i = 1;
|
| 248 |
+
node.id = `${title} ${i}`;
|
| 249 |
+
const wnodes = state.workspace.nodes!;
|
| 250 |
+
while (wnodes.find((x) => x.id === node.id)) {
|
| 251 |
+
i += 1;
|
| 252 |
+
node.id = `${title} ${i}`;
|
| 253 |
+
}
|
| 254 |
+
wnodes.push(node as WorkspaceNode);
|
| 255 |
+
setNodes([...nodes, node as WorkspaceNode]);
|
| 256 |
+
}
|
| 257 |
+
function nodeFromMeta(meta: OpsOp): Partial<WorkspaceNode> {
|
| 258 |
+
const node: Partial<WorkspaceNode> = {
|
| 259 |
+
type: meta.type,
|
| 260 |
+
data: {
|
| 261 |
+
meta: meta,
|
| 262 |
+
title: meta.name,
|
| 263 |
+
params: Object.fromEntries(
|
| 264 |
+
Object.values(meta.params).map((p) => [p.name, p.default]),
|
| 265 |
+
),
|
| 266 |
+
},
|
| 267 |
+
};
|
| 268 |
+
return node;
|
| 269 |
+
}
|
| 270 |
+
const addNodeFromSearch = useCallback(
|
| 271 |
(meta: OpsOp) => {
|
| 272 |
+
const node = nodeFromMeta(meta);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
const nss = nodeSearchSettings!;
|
| 274 |
node.position = reactFlow.screenToFlowPosition({
|
| 275 |
x: nss.pos.x,
|
| 276 |
y: nss.pos.y,
|
| 277 |
});
|
| 278 |
+
addNode(node, state, nodes);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
closeNodeSearch();
|
| 280 |
},
|
| 281 |
[nodeSearchSettings, state, reactFlow, nodes, closeNodeSearch],
|
|
|
|
| 297 |
[state],
|
| 298 |
);
|
| 299 |
const parentDir = path!.split("/").slice(0, -1).join("/");
|
| 300 |
+
function onDragOver(e: React.DragEvent<HTMLDivElement>) {
|
| 301 |
+
e.stopPropagation();
|
| 302 |
+
e.preventDefault();
|
| 303 |
+
}
|
| 304 |
+
async function onDrop(e: React.DragEvent<HTMLDivElement>) {
|
| 305 |
+
e.stopPropagation();
|
| 306 |
+
e.preventDefault();
|
| 307 |
+
const file = e.dataTransfer.files[0];
|
| 308 |
+
const formData = new FormData();
|
| 309 |
+
formData.append("file", file);
|
| 310 |
+
try {
|
| 311 |
+
await axios.post("/api/upload", formData, {
|
| 312 |
+
onUploadProgress: (progressEvent) => {
|
| 313 |
+
const percentCompleted = Math.round(
|
| 314 |
+
(100 * progressEvent.loaded) / progressEvent.total!,
|
| 315 |
+
);
|
| 316 |
+
if (percentCompleted === 100) setMessage("Processing file...");
|
| 317 |
+
else setMessage(`Uploading ${percentCompleted}%`);
|
| 318 |
+
},
|
| 319 |
+
});
|
| 320 |
+
setMessage(null);
|
| 321 |
+
const cat = catalog.data![state.workspace.env!];
|
| 322 |
+
const node = nodeFromMeta(cat["Import file"]);
|
| 323 |
+
node.position = reactFlow.screenToFlowPosition({
|
| 324 |
+
x: e.clientX,
|
| 325 |
+
y: e.clientY,
|
| 326 |
+
});
|
| 327 |
+
node.data!.params.file_path = `uploads/${file.name}`;
|
| 328 |
+
if (file.name.includes(".csv")) {
|
| 329 |
+
node.data!.params.file_format = "csv";
|
| 330 |
+
} else if (file.name.includes(".parquet")) {
|
| 331 |
+
node.data!.params.file_format = "parquet";
|
| 332 |
+
} else if (file.name.includes(".json")) {
|
| 333 |
+
node.data!.params.file_format = "json";
|
| 334 |
+
} else if (file.name.includes(".xls")) {
|
| 335 |
+
node.data!.params.file_format = "excel";
|
| 336 |
+
}
|
| 337 |
+
addNode(node, state, nodes);
|
| 338 |
+
} catch (error) {
|
| 339 |
+
setMessage("File upload failed.");
|
| 340 |
+
}
|
| 341 |
+
}
|
| 342 |
return (
|
| 343 |
<div className="workspace">
|
| 344 |
<div className="top-bar bg-neutral">
|
|
|
|
| 365 |
</a>
|
| 366 |
</div>
|
| 367 |
</div>
|
| 368 |
+
<div
|
| 369 |
+
style={{ height: "100%", width: "100vw" }}
|
| 370 |
+
onDragOver={onDragOver}
|
| 371 |
+
onDrop={onDrop}
|
| 372 |
+
>
|
| 373 |
<LynxKiteState.Provider value={state}>
|
| 374 |
<ReactFlow
|
| 375 |
nodes={nodes}
|
|
|
|
| 381 |
onPaneClick={toggleNodeSearch}
|
| 382 |
onConnect={onConnect}
|
| 383 |
proOptions={{ hideAttribution: true }}
|
| 384 |
+
maxZoom={1}
|
| 385 |
minZoom={0.3}
|
| 386 |
defaultEdgeOptions={{
|
| 387 |
markerEnd: {
|
|
|
|
| 403 |
pos={nodeSearchSettings.pos}
|
| 404 |
boxes={nodeSearchSettings.boxes}
|
| 405 |
onCancel={closeNodeSearch}
|
| 406 |
+
onAdd={addNodeFromSearch}
|
| 407 |
/>
|
| 408 |
)}
|
| 409 |
</ReactFlow>
|
| 410 |
</LynxKiteState.Provider>
|
| 411 |
+
{message && (
|
| 412 |
+
<div className="workspace-message">
|
| 413 |
+
<span className="close" onClick={() => setMessage(null)}>
|
| 414 |
+
<Close />
|
| 415 |
+
</span>
|
| 416 |
+
{message}
|
| 417 |
+
</div>
|
| 418 |
+
)}
|
| 419 |
</div>
|
| 420 |
</div>
|
| 421 |
);
|
lynxkite-app/web/tests/basic.spec.ts
CHANGED
|
@@ -21,9 +21,6 @@ test("Box creation & deletion per env", async () => {
|
|
| 21 |
const envs = await workspace.getEnvs();
|
| 22 |
for (const env of envs) {
|
| 23 |
await workspace.setEnv(env);
|
| 24 |
-
// TODO: Opening the catalog immediately after setting the env can fail.
|
| 25 |
-
// Let's fix this!
|
| 26 |
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
| 27 |
const catalog = await workspace.getCatalog();
|
| 28 |
expect(catalog).not.toHaveLength(0);
|
| 29 |
const op = catalog[0];
|
|
|
|
| 21 |
const envs = await workspace.getEnvs();
|
| 22 |
for (const env of envs) {
|
| 23 |
await workspace.setEnv(env);
|
|
|
|
|
|
|
|
|
|
| 24 |
const catalog = await workspace.getCatalog();
|
| 25 |
expect(catalog).not.toHaveLength(0);
|
| 26 |
const op = catalog[0];
|
lynxkite-app/web/tests/examples.spec.ts
CHANGED
|
@@ -17,8 +17,7 @@ test("Pytorch example", async ({ page }) => {
|
|
| 17 |
await ws.expectErrorFree();
|
| 18 |
});
|
| 19 |
|
| 20 |
-
test
|
| 21 |
-
// Fails because of missing OPENAI_API_KEY
|
| 22 |
const ws = await Workspace.open(page, "AIMO");
|
| 23 |
await ws.expectErrorFree();
|
| 24 |
});
|
|
@@ -35,12 +34,6 @@ test("Graph RAG", async ({ page }) => {
|
|
| 35 |
await ws.expectErrorFree(process.env.CI ? 2000 : 500);
|
| 36 |
});
|
| 37 |
|
| 38 |
-
test.fail("RAG chatbot app", async ({ page }) => {
|
| 39 |
-
// Fail due to all operation being unknown
|
| 40 |
-
const ws = await Workspace.open(page, "RAG chatbot app");
|
| 41 |
-
await ws.expectErrorFree();
|
| 42 |
-
});
|
| 43 |
-
|
| 44 |
test("Airlines demo", async ({ page }) => {
|
| 45 |
const ws = await Workspace.open(page, "Airlines demo");
|
| 46 |
await ws.expectErrorFree(process.env.CI ? 10000 : 500);
|
|
|
|
| 17 |
await ws.expectErrorFree();
|
| 18 |
});
|
| 19 |
|
| 20 |
+
test("AIMO example", async ({ page }) => {
|
|
|
|
| 21 |
const ws = await Workspace.open(page, "AIMO");
|
| 22 |
await ws.expectErrorFree();
|
| 23 |
});
|
|
|
|
| 34 |
await ws.expectErrorFree(process.env.CI ? 2000 : 500);
|
| 35 |
});
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
test("Airlines demo", async ({ page }) => {
|
| 38 |
const ws = await Workspace.open(page, "Airlines demo");
|
| 39 |
await ws.expectErrorFree(process.env.CI ? 10000 : 500);
|
lynxkite-app/web/tests/lynxkite.ts
CHANGED
|
@@ -59,10 +59,8 @@ export class Workspace {
|
|
| 59 |
// Avoid overlapping with existing nodes
|
| 60 |
const numNodes = allBoxes.length || 1;
|
| 61 |
await this.page.mouse.wheel(0, numNodes * 400);
|
| 62 |
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
| 63 |
}
|
| 64 |
|
| 65 |
-
// Some x,y offset, otherwise the box handle may fall outside the viewport.
|
| 66 |
await this.page.locator(".ws-name").click();
|
| 67 |
await this.page.keyboard.press("/");
|
| 68 |
await this.page
|
|
@@ -73,10 +71,11 @@ export class Workspace {
|
|
| 73 |
}
|
| 74 |
|
| 75 |
async getCatalog() {
|
| 76 |
-
await this.page.locator(".
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
|
|
|
| 80 |
// Dismiss the catalog menu
|
| 81 |
await this.page.keyboard.press("Escape");
|
| 82 |
await expect(this.page.locator(".node-search")).not.toBeVisible();
|
|
@@ -153,11 +152,6 @@ export class Workspace {
|
|
| 153 |
}
|
| 154 |
|
| 155 |
async expectErrorFree(executionWaitTime?) {
|
| 156 |
-
// TODO: Workaround, to account for workspace execution. Once
|
| 157 |
-
// we have a load indicator we can use that instead.
|
| 158 |
-
await new Promise((resolve) =>
|
| 159 |
-
setTimeout(resolve, executionWaitTime ? executionWaitTime : 500),
|
| 160 |
-
);
|
| 161 |
await expect(this.getBoxes().locator(".error").first()).not.toBeVisible();
|
| 162 |
}
|
| 163 |
|
|
@@ -188,7 +182,7 @@ export class Splash {
|
|
| 188 |
}
|
| 189 |
|
| 190 |
workspace(name: string) {
|
| 191 |
-
return this.page.getByRole("link", { name: name });
|
| 192 |
}
|
| 193 |
|
| 194 |
getEntry(name: string) {
|
|
|
|
| 59 |
// Avoid overlapping with existing nodes
|
| 60 |
const numNodes = allBoxes.length || 1;
|
| 61 |
await this.page.mouse.wheel(0, numNodes * 400);
|
|
|
|
| 62 |
}
|
| 63 |
|
|
|
|
| 64 |
await this.page.locator(".ws-name").click();
|
| 65 |
await this.page.keyboard.press("/");
|
| 66 |
await this.page
|
|
|
|
| 71 |
}
|
| 72 |
|
| 73 |
async getCatalog() {
|
| 74 |
+
await this.page.locator(".ws-name").click();
|
| 75 |
+
await this.page.keyboard.press("/");
|
| 76 |
+
const results = this.page.locator(".node-search .matches .search-result");
|
| 77 |
+
await expect(results.first()).toBeVisible();
|
| 78 |
+
const catalog = await results.allInnerTexts();
|
| 79 |
// Dismiss the catalog menu
|
| 80 |
await this.page.keyboard.press("Escape");
|
| 81 |
await expect(this.page.locator(".node-search")).not.toBeVisible();
|
|
|
|
| 152 |
}
|
| 153 |
|
| 154 |
async expectErrorFree(executionWaitTime?) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
await expect(this.getBoxes().locator(".error").first()).not.toBeVisible();
|
| 156 |
}
|
| 157 |
|
|
|
|
| 182 |
}
|
| 183 |
|
| 184 |
workspace(name: string) {
|
| 185 |
+
return this.page.getByRole("link", { name: name, exact: true });
|
| 186 |
}
|
| 187 |
|
| 188 |
getEntry(name: string) {
|
lynxkite-core/src/lynxkite/core/workspace.py
CHANGED
|
@@ -105,7 +105,8 @@ def save(ws: Workspace, path: str):
|
|
| 105 |
j = ws.model_dump()
|
| 106 |
j = json.dumps(j, indent=2, sort_keys=True) + "\n"
|
| 107 |
dirname, basename = os.path.split(path)
|
| 108 |
-
|
|
|
|
| 109 |
# Create temp file in the same directory to make sure it's on the same filesystem.
|
| 110 |
with tempfile.NamedTemporaryFile(
|
| 111 |
"w", prefix=f".{basename}.", dir=dirname, delete=False
|
|
|
|
| 105 |
j = ws.model_dump()
|
| 106 |
j = json.dumps(j, indent=2, sort_keys=True) + "\n"
|
| 107 |
dirname, basename = os.path.split(path)
|
| 108 |
+
if dirname:
|
| 109 |
+
os.makedirs(dirname, exist_ok=True)
|
| 110 |
# Create temp file in the same directory to make sure it's on the same filesystem.
|
| 111 |
with tempfile.NamedTemporaryFile(
|
| 112 |
"w", prefix=f".{basename}.", dir=dirname, delete=False
|
lynxkite-lynxscribe/src/lynxkite_lynxscribe/lynxscribe_ops.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
| 2 |
LynxScribe configuration and testing in LynxKite.
|
| 3 |
"""
|
| 4 |
|
| 5 |
-
import os
|
| 6 |
import pathlib
|
| 7 |
from lynxscribe.core.llm.base import get_llm_engine
|
| 8 |
from lynxscribe.core.vector_store.base import get_vector_store
|
|
@@ -222,11 +221,12 @@ def view(input):
|
|
| 222 |
return v
|
| 223 |
|
| 224 |
|
| 225 |
-
async def get_chat_api(ws):
|
| 226 |
from lynxkite.core import workspace
|
| 227 |
|
| 228 |
-
|
| 229 |
-
|
|
|
|
| 230 |
assert path.exists(), f"Workspace {path} does not exist"
|
| 231 |
ws = workspace.load(path)
|
| 232 |
contexts = await ops.EXECUTORS[ENV](ws)
|
|
@@ -285,19 +285,16 @@ async def api_service_get(request):
|
|
| 285 |
return {"error": "Not found"}
|
| 286 |
|
| 287 |
|
| 288 |
-
DATA_PATH = pathlib.Path(os.environ.get("LYNXKITE_DATA", "lynxkite_data"))
|
| 289 |
-
|
| 290 |
-
|
| 291 |
def get_lynxscribe_workspaces():
|
| 292 |
from lynxkite.core import workspace
|
| 293 |
|
| 294 |
workspaces = []
|
| 295 |
-
for p in
|
| 296 |
if p.is_file():
|
| 297 |
try:
|
| 298 |
ws = workspace.load(p)
|
| 299 |
if ws.env == ENV:
|
| 300 |
-
workspaces.append(p
|
| 301 |
except Exception:
|
| 302 |
pass # Ignore files that are not valid workspaces.
|
| 303 |
workspaces.sort()
|
|
|
|
| 2 |
LynxScribe configuration and testing in LynxKite.
|
| 3 |
"""
|
| 4 |
|
|
|
|
| 5 |
import pathlib
|
| 6 |
from lynxscribe.core.llm.base import get_llm_engine
|
| 7 |
from lynxscribe.core.vector_store.base import get_vector_store
|
|
|
|
| 221 |
return v
|
| 222 |
|
| 223 |
|
| 224 |
+
async def get_chat_api(ws: str):
|
| 225 |
from lynxkite.core import workspace
|
| 226 |
|
| 227 |
+
cwd = pathlib.Path()
|
| 228 |
+
path = cwd / ws
|
| 229 |
+
assert path.is_relative_to(cwd)
|
| 230 |
assert path.exists(), f"Workspace {path} does not exist"
|
| 231 |
ws = workspace.load(path)
|
| 232 |
contexts = await ops.EXECUTORS[ENV](ws)
|
|
|
|
| 285 |
return {"error": "Not found"}
|
| 286 |
|
| 287 |
|
|
|
|
|
|
|
|
|
|
| 288 |
def get_lynxscribe_workspaces():
|
| 289 |
from lynxkite.core import workspace
|
| 290 |
|
| 291 |
workspaces = []
|
| 292 |
+
for p in pathlib.Path().glob("**/*"):
|
| 293 |
if p.is_file():
|
| 294 |
try:
|
| 295 |
ws = workspace.load(p)
|
| 296 |
if ws.env == ENV:
|
| 297 |
+
workspaces.append(p)
|
| 298 |
except Exception:
|
| 299 |
pass # Ignore files that are not valid workspaces.
|
| 300 |
workspaces.sort()
|