Spaces:
Running
Running
Load ops from .py files in the data directory.
Browse files
examples/word2vec.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from lynxkite.core.ops import op
|
| 2 |
+
import staticvectors
|
| 3 |
+
import pandas as pd
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
@op("LynxKite Graph Analytics", "Word2vec for the top 1000 words", cache=True)
|
| 7 |
+
def word2vec_1000():
|
| 8 |
+
model = staticvectors.StaticVectors("neuml/word2vec-quantized")
|
| 9 |
+
with open("wordlist.txt") as f:
|
| 10 |
+
words = [w.strip() for w in f.read().strip().split("\n")]
|
| 11 |
+
df = pd.DataFrame(
|
| 12 |
+
{
|
| 13 |
+
"word": words,
|
| 14 |
+
"embedding": model.embeddings(words).tolist(),
|
| 15 |
+
}
|
| 16 |
+
)
|
| 17 |
+
return df
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@op("LynxKite Graph Analytics", "Take first N")
|
| 21 |
+
def first_n(df: pd.DataFrame, *, n=10):
|
| 22 |
+
return df.head(n)
|
lynxkite-app/src/lynxkite_app/crdt.py
CHANGED
|
@@ -233,6 +233,7 @@ async def execute(name: str, ws_crdt: pycrdt.Map, ws_pyd: workspace.Workspace, d
|
|
| 233 |
assert path.is_relative_to(cwd), "Provided workspace path is invalid"
|
| 234 |
# Save user changes before executing, in case the execution fails.
|
| 235 |
workspace.save(ws_pyd, path)
|
|
|
|
| 236 |
ws_pyd._crdt = ws_crdt
|
| 237 |
with ws_crdt.doc.transaction():
|
| 238 |
for nc, np in zip(ws_crdt["nodes"], ws_pyd.nodes):
|
|
|
|
| 233 |
assert path.is_relative_to(cwd), "Provided workspace path is invalid"
|
| 234 |
# Save user changes before executing, in case the execution fails.
|
| 235 |
workspace.save(ws_pyd, path)
|
| 236 |
+
ops.load_user_scripts(name)
|
| 237 |
ws_pyd._crdt = ws_crdt
|
| 238 |
with ws_crdt.doc.transaction():
|
| 239 |
for nc, np in zip(ws_crdt["nodes"], ws_pyd.nodes):
|
lynxkite-app/src/lynxkite_app/main.py
CHANGED
|
@@ -26,6 +26,7 @@ def detect_plugins():
|
|
| 26 |
|
| 27 |
|
| 28 |
lynxkite_plugins = detect_plugins()
|
|
|
|
| 29 |
|
| 30 |
app = fastapi.FastAPI(lifespan=crdt.lifespan)
|
| 31 |
app.include_router(crdt.router)
|
|
@@ -33,7 +34,8 @@ app.add_middleware(GZipMiddleware)
|
|
| 33 |
|
| 34 |
|
| 35 |
@app.get("/api/catalog")
|
| 36 |
-
def get_catalog():
|
|
|
|
| 37 |
return {k: {op.name: op.model_dump() for op in v.values()} for k, v in ops.CATALOGS.items()}
|
| 38 |
|
| 39 |
|
|
|
|
| 26 |
|
| 27 |
|
| 28 |
lynxkite_plugins = detect_plugins()
|
| 29 |
+
ops.save_catalogs("plugins loaded")
|
| 30 |
|
| 31 |
app = fastapi.FastAPI(lifespan=crdt.lifespan)
|
| 32 |
app.include_router(crdt.router)
|
|
|
|
| 34 |
|
| 35 |
|
| 36 |
@app.get("/api/catalog")
|
| 37 |
+
def get_catalog(workspace: str):
|
| 38 |
+
ops.load_user_scripts(workspace)
|
| 39 |
return {k: {op.name: op.model_dump() for op in v.values()} for k, v in ops.CATALOGS.items()}
|
| 40 |
|
| 41 |
|
lynxkite-app/web/src/workspace/Workspace.tsx
CHANGED
|
@@ -151,7 +151,7 @@ function LynxKiteFlow() {
|
|
| 151 |
|
| 152 |
const fetcher: Fetcher<Catalogs> = (resource: string, init?: RequestInit) =>
|
| 153 |
fetch(resource, init).then((res) => res.json());
|
| 154 |
-
const catalog = useSWR(
|
| 155 |
const [suppressSearchUntil, setSuppressSearchUntil] = useState(0);
|
| 156 |
const [nodeSearchSettings, setNodeSearchSettings] = useState(
|
| 157 |
undefined as
|
|
|
|
| 151 |
|
| 152 |
const fetcher: Fetcher<Catalogs> = (resource: string, init?: RequestInit) =>
|
| 153 |
fetch(resource, init).then((res) => res.json());
|
| 154 |
+
const catalog = useSWR(`/api/catalog?workspace=${path}`, fetcher);
|
| 155 |
const [suppressSearchUntil, setSuppressSearchUntil] = useState(0);
|
| 156 |
const [nodeSearchSettings, setNodeSearchSettings] = useState(
|
| 157 |
undefined as
|
lynxkite-core/src/lynxkite/core/ops.py
CHANGED
|
@@ -4,7 +4,11 @@ from __future__ import annotations
|
|
| 4 |
import asyncio
|
| 5 |
import enum
|
| 6 |
import functools
|
|
|
|
| 7 |
import inspect
|
|
|
|
|
|
|
|
|
|
| 8 |
import types
|
| 9 |
import pydantic
|
| 10 |
import typing
|
|
@@ -14,8 +18,11 @@ from typing_extensions import Annotated
|
|
| 14 |
if typing.TYPE_CHECKING:
|
| 15 |
from . import workspace
|
| 16 |
|
| 17 |
-
|
|
|
|
|
|
|
| 18 |
EXECUTORS = {}
|
|
|
|
| 19 |
|
| 20 |
typeof = type # We have some arguments called "type".
|
| 21 |
|
|
@@ -189,10 +196,12 @@ class Op(BaseConfig):
|
|
| 189 |
return res
|
| 190 |
|
| 191 |
|
| 192 |
-
def op(env: str, name: str, *, view="basic", outputs=None, params=None):
|
| 193 |
"""Decorator for defining an operation."""
|
| 194 |
|
| 195 |
def decorator(func):
|
|
|
|
|
|
|
| 196 |
sig = inspect.signature(func)
|
| 197 |
# Positional arguments are inputs.
|
| 198 |
inputs = {
|
|
@@ -308,3 +317,41 @@ def slow(func):
|
|
| 308 |
return await asyncio.to_thread(func, *args, **kwargs)
|
| 309 |
|
| 310 |
return wrapper
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
import asyncio
|
| 5 |
import enum
|
| 6 |
import functools
|
| 7 |
+
import importlib
|
| 8 |
import inspect
|
| 9 |
+
import pathlib
|
| 10 |
+
import traceback
|
| 11 |
+
import joblib
|
| 12 |
import types
|
| 13 |
import pydantic
|
| 14 |
import typing
|
|
|
|
| 18 |
if typing.TYPE_CHECKING:
|
| 19 |
from . import workspace
|
| 20 |
|
| 21 |
+
Catalog = dict[str, "Op"]
|
| 22 |
+
Catalogs = dict[str, Catalog]
|
| 23 |
+
CATALOGS: Catalogs = {}
|
| 24 |
EXECUTORS = {}
|
| 25 |
+
mem = joblib.Memory(".joblib-cache")
|
| 26 |
|
| 27 |
typeof = type # We have some arguments called "type".
|
| 28 |
|
|
|
|
| 196 |
return res
|
| 197 |
|
| 198 |
|
| 199 |
+
def op(env: str, name: str, *, view="basic", outputs=None, params=None, cache=False):
|
| 200 |
"""Decorator for defining an operation."""
|
| 201 |
|
| 202 |
def decorator(func):
|
| 203 |
+
if cache:
|
| 204 |
+
func = mem.cache(func)
|
| 205 |
sig = inspect.signature(func)
|
| 206 |
# Positional arguments are inputs.
|
| 207 |
inputs = {
|
|
|
|
| 317 |
return await asyncio.to_thread(func, *args, **kwargs)
|
| 318 |
|
| 319 |
return wrapper
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
CATALOGS_SNAPSHOTS: dict[str, Catalogs] = {}
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
def save_catalogs(snapshot_name: str):
|
| 326 |
+
CATALOGS_SNAPSHOTS[snapshot_name] = {k: dict(v) for k, v in CATALOGS.items()}
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
def load_catalogs(snapshot_name: str):
|
| 330 |
+
global CATALOGS
|
| 331 |
+
snap = CATALOGS_SNAPSHOTS[snapshot_name]
|
| 332 |
+
CATALOGS = {k: dict(v) for k, v in snap.items()}
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
def load_user_scripts(workspace: str):
|
| 336 |
+
"""Reloads the *.py in the workspace's directory and higher-level directories."""
|
| 337 |
+
if "plugins loaded" in CATALOGS_SNAPSHOTS:
|
| 338 |
+
load_catalogs("plugins loaded")
|
| 339 |
+
cwd = pathlib.Path()
|
| 340 |
+
path = cwd / workspace
|
| 341 |
+
assert path.is_relative_to(cwd), "Provided workspace path is invalid"
|
| 342 |
+
for p in path.parents:
|
| 343 |
+
print("checking user scripts in", p)
|
| 344 |
+
for f in p.glob("*.py"):
|
| 345 |
+
try:
|
| 346 |
+
run_user_script(f)
|
| 347 |
+
except Exception:
|
| 348 |
+
traceback.print_exc()
|
| 349 |
+
if p == cwd:
|
| 350 |
+
break
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
def run_user_script(script_path: pathlib.Path):
|
| 354 |
+
print(f"Running {script_path}...")
|
| 355 |
+
spec = importlib.util.spec_from_file_location(script_path.stem, str(script_path))
|
| 356 |
+
module = importlib.util.module_from_spec(spec)
|
| 357 |
+
spec.loader.exec_module(module)
|