Spaces:
Running
Running
Merge pull request #12 from lynxkite/darabos-shadows
Browse files- lynxkite-app/tests/test_main.py +2 -0
- lynxkite-app/uv.lock +0 -0
- lynxkite-app/web/src/apiTypes.ts +2 -0
- lynxkite-app/web/src/index.css +1 -0
- lynxkite-app/web/src/workspace/Workspace.tsx +48 -4
- lynxkite-app/web/src/workspace/nodes/NodeParameter.tsx +1 -1
- lynxkite-app/web/tests/lynxkite.ts +1 -7
- lynxkite-core/src/lynxkite/core/workspace.py +18 -17
- lynxkite-core/tests/test_workspace.py +16 -28
- lynxkite-graph-analytics/tests/test_lynxkite_ops.py +40 -56
- lynxkite-graph-analytics/tests/test_pytorch_model_ops.py +3 -10
lynxkite-app/tests/test_main.py
CHANGED
@@ -44,6 +44,8 @@ def test_save_and_load():
|
|
44 |
"params": {"param1": "value"},
|
45 |
},
|
46 |
"position": {"x": -493.5496596237119, "y": 20.90123252513356},
|
|
|
|
|
47 |
}
|
48 |
],
|
49 |
"edges": [],
|
|
|
44 |
"params": {"param1": "value"},
|
45 |
},
|
46 |
"position": {"x": -493.5496596237119, "y": 20.90123252513356},
|
47 |
+
"width": 100,
|
48 |
+
"height": 100,
|
49 |
}
|
50 |
],
|
51 |
"edges": [],
|
lynxkite-app/uv.lock
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
lynxkite-app/web/src/apiTypes.ts
CHANGED
@@ -34,6 +34,8 @@ export interface WorkspaceNode {
|
|
34 |
type: string;
|
35 |
data: WorkspaceNodeData;
|
36 |
position: Position;
|
|
|
|
|
37 |
[k: string]: unknown;
|
38 |
}
|
39 |
export interface WorkspaceNodeData {
|
|
|
34 |
type: string;
|
35 |
data: WorkspaceNodeData;
|
36 |
position: Position;
|
37 |
+
width: number;
|
38 |
+
height: number;
|
39 |
[k: string]: unknown;
|
40 |
}
|
41 |
export interface WorkspaceNodeData {
|
lynxkite-app/web/src/index.css
CHANGED
@@ -76,6 +76,7 @@ body {
|
|
76 |
.lynxkite-node {
|
77 |
box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.3);
|
78 |
border-radius: 4px;
|
|
|
79 |
background: white;
|
80 |
display: flex;
|
81 |
flex-direction: column;
|
|
|
76 |
.lynxkite-node {
|
77 |
box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.3);
|
78 |
border-radius: 4px;
|
79 |
+
overflow: hidden;
|
80 |
background: white;
|
81 |
display: flex;
|
82 |
flex-direction: column;
|
lynxkite-app/web/src/workspace/Workspace.tsx
CHANGED
@@ -16,7 +16,7 @@ import {
|
|
16 |
useUpdateNodeInternals,
|
17 |
} from "@xyflow/react";
|
18 |
import axios from "axios";
|
19 |
-
import { type MouseEvent, useCallback, useEffect, useMemo, useState } from "react";
|
20 |
import { Link } from "react-router";
|
21 |
import useSWR, { type Fetcher } from "swr";
|
22 |
import { WebsocketProvider } from "y-websocket";
|
@@ -61,6 +61,7 @@ export default function Workspace(props: any) {
|
|
61 |
function LynxKiteFlow() {
|
62 |
const updateNodeInternals = useUpdateNodeInternals();
|
63 |
const reactFlow = useReactFlow();
|
|
|
64 |
const [nodes, setNodes] = useState([] as Node[]);
|
65 |
const [edges, setEdges] = useState([] as Edge[]);
|
66 |
const path = usePath().replace(/^[/]edit[/]/, "");
|
@@ -210,7 +211,7 @@ function LynxKiteFlow() {
|
|
210 |
if (event.key === "/") {
|
211 |
event.preventDefault();
|
212 |
setNodeSearchSettings({
|
213 |
-
pos:
|
214 |
boxes: catalog.data![state.workspace.env!],
|
215 |
});
|
216 |
} else if (event.key === "r") {
|
@@ -225,6 +226,39 @@ function LynxKiteFlow() {
|
|
225 |
};
|
226 |
}, [catalog.data, nodeSearchSettings, state.workspace.env]);
|
227 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
function isTypingInFormElement() {
|
229 |
const activeElement = document.activeElement;
|
230 |
return (
|
@@ -504,7 +538,12 @@ function LynxKiteFlow() {
|
|
504 |
</Tooltip>
|
505 |
</div>
|
506 |
</div>
|
507 |
-
<div
|
|
|
|
|
|
|
|
|
|
|
508 |
<LynxKiteState.Provider value={state}>
|
509 |
<ReactFlow
|
510 |
nodes={nodes}
|
@@ -517,9 +556,13 @@ function LynxKiteFlow() {
|
|
517 |
onPaneClick={toggleNodeSearch}
|
518 |
onConnect={onConnect}
|
519 |
proOptions={{ hideAttribution: true }}
|
520 |
-
maxZoom={
|
521 |
minZoom={0.2}
|
522 |
zoomOnScroll={false}
|
|
|
|
|
|
|
|
|
523 |
preventScrolling={false}
|
524 |
defaultEdgeOptions={{
|
525 |
markerEnd: {
|
@@ -533,6 +576,7 @@ function LynxKiteFlow() {
|
|
533 |
stroke: "black",
|
534 |
},
|
535 |
}}
|
|
|
536 |
>
|
537 |
<Controls />
|
538 |
{nodeSearchSettings && (
|
|
|
16 |
useUpdateNodeInternals,
|
17 |
} from "@xyflow/react";
|
18 |
import axios from "axios";
|
19 |
+
import { type MouseEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
20 |
import { Link } from "react-router";
|
21 |
import useSWR, { type Fetcher } from "swr";
|
22 |
import { WebsocketProvider } from "y-websocket";
|
|
|
61 |
function LynxKiteFlow() {
|
62 |
const updateNodeInternals = useUpdateNodeInternals();
|
63 |
const reactFlow = useReactFlow();
|
64 |
+
const reactFlowContainer = useRef<HTMLDivElement>(null);
|
65 |
const [nodes, setNodes] = useState([] as Node[]);
|
66 |
const [edges, setEdges] = useState([] as Edge[]);
|
67 |
const path = usePath().replace(/^[/]edit[/]/, "");
|
|
|
211 |
if (event.key === "/") {
|
212 |
event.preventDefault();
|
213 |
setNodeSearchSettings({
|
214 |
+
pos: getBestPosition(),
|
215 |
boxes: catalog.data![state.workspace.env!],
|
216 |
});
|
217 |
} else if (event.key === "r") {
|
|
|
226 |
};
|
227 |
}, [catalog.data, nodeSearchSettings, state.workspace.env]);
|
228 |
|
229 |
+
function getBestPosition() {
|
230 |
+
const W = reactFlowContainer.current!.clientWidth;
|
231 |
+
const H = reactFlowContainer.current!.clientHeight;
|
232 |
+
const w = 200;
|
233 |
+
const h = 200;
|
234 |
+
const SPEED = 20;
|
235 |
+
const GAP = 50;
|
236 |
+
const pos = { x: 100, y: 100 };
|
237 |
+
while (pos.y < H) {
|
238 |
+
// Find a position that is not occupied by a node.
|
239 |
+
const fpos = reactFlow.screenToFlowPosition(pos);
|
240 |
+
const occupied = state.workspace.nodes!.some((n) => {
|
241 |
+
const np = n.position;
|
242 |
+
return (
|
243 |
+
np.x < fpos.x + w + GAP &&
|
244 |
+
np.x + n.width + GAP > fpos.x &&
|
245 |
+
np.y < fpos.y + h + GAP &&
|
246 |
+
np.y + n.height + GAP > fpos.y
|
247 |
+
);
|
248 |
+
});
|
249 |
+
if (!occupied) {
|
250 |
+
return pos;
|
251 |
+
}
|
252 |
+
// Move the position to the right and down until we find a free spot.
|
253 |
+
pos.x += SPEED;
|
254 |
+
if (pos.x + w > W) {
|
255 |
+
pos.x = 100;
|
256 |
+
pos.y += SPEED;
|
257 |
+
}
|
258 |
+
}
|
259 |
+
return { x: 100, y: 100 };
|
260 |
+
}
|
261 |
+
|
262 |
function isTypingInFormElement() {
|
263 |
const activeElement = document.activeElement;
|
264 |
return (
|
|
|
538 |
</Tooltip>
|
539 |
</div>
|
540 |
</div>
|
541 |
+
<div
|
542 |
+
style={{ height: "100%", width: "100vw" }}
|
543 |
+
onDragOver={onDragOver}
|
544 |
+
onDrop={onDrop}
|
545 |
+
ref={reactFlowContainer}
|
546 |
+
>
|
547 |
<LynxKiteState.Provider value={state}>
|
548 |
<ReactFlow
|
549 |
nodes={nodes}
|
|
|
556 |
onPaneClick={toggleNodeSearch}
|
557 |
onConnect={onConnect}
|
558 |
proOptions={{ hideAttribution: true }}
|
559 |
+
maxZoom={10}
|
560 |
minZoom={0.2}
|
561 |
zoomOnScroll={false}
|
562 |
+
panOnScroll={true}
|
563 |
+
panOnDrag={false}
|
564 |
+
selectionOnDrag={true}
|
565 |
+
panOnScrollSpeed={1}
|
566 |
preventScrolling={false}
|
567 |
defaultEdgeOptions={{
|
568 |
markerEnd: {
|
|
|
576 |
stroke: "black",
|
577 |
},
|
578 |
}}
|
579 |
+
fitViewOptions={{ maxZoom: 1 }}
|
580 |
>
|
581 |
<Controls />
|
582 |
{nodeSearchSettings && (
|
lynxkite-app/web/src/workspace/nodes/NodeParameter.tsx
CHANGED
@@ -237,7 +237,7 @@ export default function NodeParameter({ name, value, meta, data, setParam }: Nod
|
|
237 |
<textarea
|
238 |
className="textarea textarea-bordered w-full"
|
239 |
rows={6}
|
240 |
-
value={value}
|
241 |
onChange={(evt) => onChange(evt.currentTarget.value, { delay: 2 })}
|
242 |
onBlur={(evt) => onChange(evt.currentTarget.value, { delay: 0 })}
|
243 |
/>
|
|
|
237 |
<textarea
|
238 |
className="textarea textarea-bordered w-full"
|
239 |
rows={6}
|
240 |
+
value={value || ""}
|
241 |
onChange={(evt) => onChange(evt.currentTarget.value, { delay: 2 })}
|
242 |
onBlur={(evt) => onChange(evt.currentTarget.value, { delay: 0 })}
|
243 |
/>
|
lynxkite-app/web/tests/lynxkite.ts
CHANGED
@@ -53,14 +53,8 @@ export class Workspace {
|
|
53 |
}
|
54 |
|
55 |
async addBox(boxName) {
|
56 |
-
//TODO: Support passing box parameters
|
57 |
const allBoxes = await this.getBoxes().all();
|
58 |
-
if (allBoxes) {
|
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.locator(".node-search").getByText(boxName, { exact: true }).click();
|
|
|
53 |
}
|
54 |
|
55 |
async addBox(boxName) {
|
56 |
+
// TODO: Support passing box parameters.
|
57 |
const allBoxes = await this.getBoxes().all();
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
await this.page.locator(".ws-name").click();
|
59 |
await this.page.keyboard.press("/");
|
60 |
await this.page.locator(".node-search").getByText(boxName, { exact: true }).click();
|
lynxkite-core/src/lynxkite/core/workspace.py
CHANGED
@@ -40,12 +40,13 @@ class WorkspaceNodeData(BaseConfig):
|
|
40 |
|
41 |
|
42 |
class WorkspaceNode(BaseConfig):
|
43 |
-
#
|
44 |
-
# modyfing them will break the frontend.
|
45 |
id: str
|
46 |
type: str
|
47 |
data: WorkspaceNodeData
|
48 |
position: Position
|
|
|
|
|
49 |
_crdt: pycrdt.Map
|
50 |
|
51 |
def publish_started(self):
|
@@ -202,27 +203,27 @@ class Workspace(BaseConfig):
|
|
202 |
nc["data"] = pycrdt.Map()
|
203 |
np._crdt = nc
|
204 |
|
205 |
-
def add_node(self, func):
|
206 |
"""For convenience in e.g. tests."""
|
207 |
random_string = os.urandom(4).hex()
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
status=NodeStatus.planned,
|
218 |
-
),
|
219 |
-
position=Position(x=0, y=0),
|
220 |
-
)
|
221 |
self.nodes.append(node)
|
222 |
return node
|
223 |
|
224 |
def add_edge(
|
225 |
-
self,
|
|
|
|
|
|
|
|
|
226 |
):
|
227 |
"""For convenience in e.g. tests."""
|
228 |
edge = WorkspaceEdge(
|
|
|
40 |
|
41 |
|
42 |
class WorkspaceNode(BaseConfig):
|
43 |
+
# Most of these fields are shared with ReactFlow.
|
|
|
44 |
id: str
|
45 |
type: str
|
46 |
data: WorkspaceNodeData
|
47 |
position: Position
|
48 |
+
width: float
|
49 |
+
height: float
|
50 |
_crdt: pycrdt.Map
|
51 |
|
52 |
def publish_started(self):
|
|
|
203 |
nc["data"] = pycrdt.Map()
|
204 |
np._crdt = nc
|
205 |
|
206 |
+
def add_node(self, func=None, **kwargs):
|
207 |
"""For convenience in e.g. tests."""
|
208 |
random_string = os.urandom(4).hex()
|
209 |
+
if func:
|
210 |
+
kwargs["type"] = func.__op__.type
|
211 |
+
kwargs["data"] = WorkspaceNodeData(title=func.__op__.name, params={})
|
212 |
+
kwargs.setdefault("type", "basic")
|
213 |
+
kwargs.setdefault("id", f"{kwargs['data'].title} {random_string}")
|
214 |
+
kwargs.setdefault("position", Position(x=0, y=0))
|
215 |
+
kwargs.setdefault("width", 100)
|
216 |
+
kwargs.setdefault("height", 100)
|
217 |
+
node = WorkspaceNode(**kwargs)
|
|
|
|
|
|
|
|
|
218 |
self.nodes.append(node)
|
219 |
return node
|
220 |
|
221 |
def add_edge(
|
222 |
+
self,
|
223 |
+
source: WorkspaceNode,
|
224 |
+
sourceHandle: str,
|
225 |
+
target: WorkspaceNode,
|
226 |
+
targetHandle: str,
|
227 |
):
|
228 |
"""For convenience in e.g. tests."""
|
229 |
edge = WorkspaceEdge(
|
lynxkite-core/tests/test_workspace.py
CHANGED
@@ -6,21 +6,15 @@ from lynxkite.core import workspace
|
|
6 |
|
7 |
def test_save_load():
|
8 |
ws = workspace.Workspace(env="test")
|
9 |
-
ws.
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
data=workspace.WorkspaceNodeData(title="Node 1", params={}),
|
14 |
-
position=workspace.Position(x=0, y=0),
|
15 |
-
)
|
16 |
)
|
17 |
-
ws.
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
data=workspace.WorkspaceNodeData(title="Node 2", params={}),
|
22 |
-
position=workspace.Position(x=0, y=0),
|
23 |
-
)
|
24 |
)
|
25 |
ws.edges.append(
|
26 |
workspace.WorkspaceEdge(
|
@@ -72,21 +66,15 @@ def populate_ops_catalog():
|
|
72 |
|
73 |
def test_update_metadata():
|
74 |
ws = workspace.Workspace(env="test")
|
75 |
-
ws.
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
data=workspace.WorkspaceNodeData(title="Test Operation", params={}),
|
80 |
-
position=workspace.Position(x=0, y=0),
|
81 |
-
)
|
82 |
)
|
83 |
-
ws.
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
data=workspace.WorkspaceNodeData(title="Unknown Operation", params={}),
|
88 |
-
position=workspace.Position(x=0, y=0),
|
89 |
-
)
|
90 |
)
|
91 |
ws.update_metadata()
|
92 |
assert ws.nodes[0].data.meta.name == "Test Operation"
|
|
|
6 |
|
7 |
def test_save_load():
|
8 |
ws = workspace.Workspace(env="test")
|
9 |
+
ws.add_node(
|
10 |
+
id="1",
|
11 |
+
type="node_type",
|
12 |
+
data=workspace.WorkspaceNodeData(title="Node 1", params={}),
|
|
|
|
|
|
|
13 |
)
|
14 |
+
ws.add_node(
|
15 |
+
id="2",
|
16 |
+
type="node_type",
|
17 |
+
data=workspace.WorkspaceNodeData(title="Node 2", params={}),
|
|
|
|
|
|
|
18 |
)
|
19 |
ws.edges.append(
|
20 |
workspace.WorkspaceEdge(
|
|
|
66 |
|
67 |
def test_update_metadata():
|
68 |
ws = workspace.Workspace(env="test")
|
69 |
+
ws.add_node(
|
70 |
+
id="1",
|
71 |
+
type="basic",
|
72 |
+
data=workspace.WorkspaceNodeData(title="Test Operation", params={}),
|
|
|
|
|
|
|
73 |
)
|
74 |
+
ws.add_node(
|
75 |
+
id="2",
|
76 |
+
type="basic",
|
77 |
+
data=workspace.WorkspaceNodeData(title="Unknown Operation", params={}),
|
|
|
|
|
|
|
78 |
)
|
79 |
ws.update_metadata()
|
80 |
assert ws.nodes[0].data.meta.name == "Test Operation"
|
lynxkite-graph-analytics/tests/test_lynxkite_ops.py
CHANGED
@@ -8,13 +8,11 @@ from lynxkite_graph_analytics.core import Bundle, execute, ENV
|
|
8 |
|
9 |
async def test_execute_operation_not_in_catalog():
|
10 |
ws = workspace.Workspace(env=ENV)
|
11 |
-
ws.
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
position=workspace.Position(x=0, y=0),
|
17 |
-
)
|
18 |
)
|
19 |
await execute(ws)
|
20 |
assert ws.nodes[0].data.error == "Operation not found in catalog"
|
@@ -43,37 +41,29 @@ async def test_execute_operation_inputs_correct_cast():
|
|
43 |
return bundle
|
44 |
|
45 |
ws = workspace.Workspace(env="test")
|
46 |
-
ws.
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
position=workspace.Position(x=0, y=0),
|
52 |
-
)
|
53 |
)
|
54 |
-
ws.
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
position=workspace.Position(x=100, y=0),
|
60 |
-
)
|
61 |
)
|
62 |
-
ws.
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
position=workspace.Position(x=200, y=0),
|
68 |
-
)
|
69 |
)
|
70 |
-
ws.
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
position=workspace.Position(x=300, y=0),
|
76 |
-
)
|
77 |
)
|
78 |
ws.edges = [
|
79 |
workspace.WorkspaceEdge(
|
@@ -109,29 +99,23 @@ async def test_multiple_inputs():
|
|
109 |
return a < b
|
110 |
|
111 |
ws = workspace.Workspace(env="test")
|
112 |
-
ws.
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
position=workspace.Position(x=0, y=0),
|
118 |
-
)
|
119 |
)
|
120 |
-
ws.
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
position=workspace.Position(x=100, y=0),
|
126 |
-
)
|
127 |
)
|
128 |
-
ws.
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
position=workspace.Position(x=200, y=0),
|
134 |
-
)
|
135 |
)
|
136 |
ws.edges = [
|
137 |
workspace.WorkspaceEdge(
|
|
|
8 |
|
9 |
async def test_execute_operation_not_in_catalog():
|
10 |
ws = workspace.Workspace(env=ENV)
|
11 |
+
ws.add_node(
|
12 |
+
id="1",
|
13 |
+
type="node_type",
|
14 |
+
data=workspace.WorkspaceNodeData(title="Non existing op", params={}),
|
15 |
+
position=workspace.Position(x=0, y=0),
|
|
|
|
|
16 |
)
|
17 |
await execute(ws)
|
18 |
assert ws.nodes[0].data.error == "Operation not found in catalog"
|
|
|
41 |
return bundle
|
42 |
|
43 |
ws = workspace.Workspace(env="test")
|
44 |
+
ws.add_node(
|
45 |
+
id="1",
|
46 |
+
type="node_type",
|
47 |
+
data=workspace.WorkspaceNodeData(title="Create Bundle", params={}),
|
48 |
+
position=workspace.Position(x=0, y=0),
|
|
|
|
|
49 |
)
|
50 |
+
ws.add_node(
|
51 |
+
id="2",
|
52 |
+
type="node_type",
|
53 |
+
data=workspace.WorkspaceNodeData(title="Bundle to Graph", params={}),
|
54 |
+
position=workspace.Position(x=100, y=0),
|
|
|
|
|
55 |
)
|
56 |
+
ws.add_node(
|
57 |
+
id="3",
|
58 |
+
type="node_type",
|
59 |
+
data=workspace.WorkspaceNodeData(title="Graph to Bundle", params={}),
|
60 |
+
position=workspace.Position(x=200, y=0),
|
|
|
|
|
61 |
)
|
62 |
+
ws.add_node(
|
63 |
+
id="4",
|
64 |
+
type="node_type",
|
65 |
+
data=workspace.WorkspaceNodeData(title="Dataframe to Bundle", params={}),
|
66 |
+
position=workspace.Position(x=300, y=0),
|
|
|
|
|
67 |
)
|
68 |
ws.edges = [
|
69 |
workspace.WorkspaceEdge(
|
|
|
99 |
return a < b
|
100 |
|
101 |
ws = workspace.Workspace(env="test")
|
102 |
+
ws.add_node(
|
103 |
+
id="one",
|
104 |
+
type="cool",
|
105 |
+
data=workspace.WorkspaceNodeData(title="One", params={}),
|
106 |
+
position=workspace.Position(x=0, y=0),
|
|
|
|
|
107 |
)
|
108 |
+
ws.add_node(
|
109 |
+
id="two",
|
110 |
+
type="cool",
|
111 |
+
data=workspace.WorkspaceNodeData(title="Two", params={}),
|
112 |
+
position=workspace.Position(x=100, y=0),
|
|
|
|
|
113 |
)
|
114 |
+
ws.add_node(
|
115 |
+
id="smaller",
|
116 |
+
type="cool",
|
117 |
+
data=workspace.WorkspaceNodeData(title="Smaller?", params={}),
|
118 |
+
position=workspace.Position(x=200, y=0),
|
|
|
|
|
119 |
)
|
120 |
ws.edges = [
|
121 |
workspace.WorkspaceEdge(
|
lynxkite-graph-analytics/tests/test_pytorch_model_ops.py
CHANGED
@@ -9,16 +9,9 @@ def make_ws(env, nodes: dict[str, dict], edges: list[tuple[str, str]]):
|
|
9 |
for id, data in nodes.items():
|
10 |
title = data["title"]
|
11 |
del data["title"]
|
12 |
-
ws.
|
13 |
-
|
14 |
-
|
15 |
-
type="basic",
|
16 |
-
data=workspace.WorkspaceNodeData(title=title, params=data),
|
17 |
-
position=workspace.Position(
|
18 |
-
x=data.get("x", 0),
|
19 |
-
y=data.get("y", 0),
|
20 |
-
),
|
21 |
-
)
|
22 |
)
|
23 |
ws.edges = [
|
24 |
workspace.WorkspaceEdge(
|
|
|
9 |
for id, data in nodes.items():
|
10 |
title = data["title"]
|
11 |
del data["title"]
|
12 |
+
ws.add_node(
|
13 |
+
id=id,
|
14 |
+
data=workspace.WorkspaceNodeData(title=title, params=data),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
)
|
16 |
ws.edges = [
|
17 |
workspace.WorkspaceEdge(
|