Spaces:
Running
Running
Update node metadata on execution.
Browse files
lynxkite-app/src/lynxkite_app/crdt.py
CHANGED
|
@@ -205,7 +205,7 @@ def try_to_load_workspace(ws: pycrdt.Map, name: str):
|
|
| 205 |
ws,
|
| 206 |
ws_pyd.model_dump(),
|
| 207 |
# We treat some fields as black boxes. They are not edited on the frontend.
|
| 208 |
-
non_collaborative_fields={"display", "input_metadata"},
|
| 209 |
)
|
| 210 |
|
| 211 |
|
|
@@ -264,17 +264,14 @@ async def execute(name: str, ws_crdt: pycrdt.Map, ws_pyd: workspace.Workspace, d
|
|
| 264 |
assert path.is_relative_to(cwd), "Provided workspace path is invalid"
|
| 265 |
# Save user changes before executing, in case the execution fails.
|
| 266 |
workspace.save(ws_pyd, path)
|
|
|
|
|
|
|
|
|
|
| 267 |
if not workspace.has_executor(ws_pyd):
|
| 268 |
return
|
| 269 |
-
ops.load_user_scripts(name)
|
| 270 |
-
ws_pyd._crdt = ws_crdt
|
| 271 |
with ws_crdt.doc.transaction():
|
| 272 |
-
for nc
|
| 273 |
-
if "data" not in nc:
|
| 274 |
-
nc["data"] = pycrdt.Map()
|
| 275 |
nc["data"]["status"] = "planned"
|
| 276 |
-
# Nodes get a reference to their CRDT maps, so they can update them as the results come in.
|
| 277 |
-
np._crdt = nc
|
| 278 |
ws_pyd = ws_pyd.normalize()
|
| 279 |
await workspace.execute(ws_pyd)
|
| 280 |
workspace.save(ws_pyd, path)
|
|
|
|
| 205 |
ws,
|
| 206 |
ws_pyd.model_dump(),
|
| 207 |
# We treat some fields as black boxes. They are not edited on the frontend.
|
| 208 |
+
non_collaborative_fields={"display", "input_metadata", "meta"},
|
| 209 |
)
|
| 210 |
|
| 211 |
|
|
|
|
| 264 |
assert path.is_relative_to(cwd), "Provided workspace path is invalid"
|
| 265 |
# Save user changes before executing, in case the execution fails.
|
| 266 |
workspace.save(ws_pyd, path)
|
| 267 |
+
ops.load_user_scripts(name)
|
| 268 |
+
workspace.connect_crdt(ws_pyd, ws_crdt)
|
| 269 |
+
workspace.update_metadata(ws_pyd)
|
| 270 |
if not workspace.has_executor(ws_pyd):
|
| 271 |
return
|
|
|
|
|
|
|
| 272 |
with ws_crdt.doc.transaction():
|
| 273 |
+
for nc in ws_crdt["nodes"]:
|
|
|
|
|
|
|
| 274 |
nc["data"]["status"] = "planned"
|
|
|
|
|
|
|
| 275 |
ws_pyd = ws_pyd.normalize()
|
| 276 |
await workspace.execute(ws_pyd)
|
| 277 |
workspace.save(ws_pyd, path)
|
lynxkite-app/web/src/workspace/nodes/LynxKiteNode.tsx
CHANGED
|
@@ -53,7 +53,7 @@ function LynxKiteNodeComponent(props: LynxKiteNodeProps) {
|
|
| 53 |
const reactFlow = useReactFlow();
|
| 54 |
const data = props.data;
|
| 55 |
const expanded = !data.collapsed;
|
| 56 |
-
const handles = getHandles(data.meta?.inputs || {}, data.meta?.outputs || {});
|
| 57 |
function titleClicked() {
|
| 58 |
reactFlow.updateNodeData(props.id, { collapsed: expanded });
|
| 59 |
}
|
|
@@ -64,8 +64,8 @@ function LynxKiteNodeComponent(props: LynxKiteNodeProps) {
|
|
| 64 |
right: "top",
|
| 65 |
};
|
| 66 |
const titleStyle: { backgroundColor?: string } = {};
|
| 67 |
-
if (data.meta?.color) {
|
| 68 |
-
titleStyle.backgroundColor = OP_COLORS[data.meta.color] || data.meta.color;
|
| 69 |
}
|
| 70 |
return (
|
| 71 |
<div
|
|
|
|
| 53 |
const reactFlow = useReactFlow();
|
| 54 |
const data = props.data;
|
| 55 |
const expanded = !data.collapsed;
|
| 56 |
+
const handles = getHandles(data.meta?.value?.inputs || {}, data.meta?.value?.outputs || {});
|
| 57 |
function titleClicked() {
|
| 58 |
reactFlow.updateNodeData(props.id, { collapsed: expanded });
|
| 59 |
}
|
|
|
|
| 64 |
right: "top",
|
| 65 |
};
|
| 66 |
const titleStyle: { backgroundColor?: string } = {};
|
| 67 |
+
if (data.meta?.value?.color) {
|
| 68 |
+
titleStyle.backgroundColor = OP_COLORS[data.meta.value.color] || data.meta.value.color;
|
| 69 |
}
|
| 70 |
return (
|
| 71 |
<div
|
lynxkite-app/web/src/workspace/nodes/NodeWithParams.tsx
CHANGED
|
@@ -10,7 +10,7 @@ export type UpdateOptions = { delay?: number };
|
|
| 10 |
|
| 11 |
export function NodeWithParams(props: any) {
|
| 12 |
const reactFlow = useReactFlow();
|
| 13 |
-
const metaParams = props.data.meta?.params;
|
| 14 |
const [collapsed, setCollapsed] = React.useState(props.collapsed);
|
| 15 |
|
| 16 |
function setParam(name: string, newValue: any, opts: UpdateOptions) {
|
|
|
|
| 10 |
|
| 11 |
export function NodeWithParams(props: any) {
|
| 12 |
const reactFlow = useReactFlow();
|
| 13 |
+
const metaParams = props.data.meta?.value?.params;
|
| 14 |
const [collapsed, setCollapsed] = React.useState(props.collapsed);
|
| 15 |
|
| 16 |
function setParam(name: string, newValue: any, opts: UpdateOptions) {
|
lynxkite-core/src/lynxkite/core/workspace.py
CHANGED
|
@@ -158,11 +158,11 @@ def load(path: str) -> Workspace:
|
|
| 158 |
j = f.read()
|
| 159 |
ws = Workspace.model_validate_json(j)
|
| 160 |
# Metadata is added after loading. This way code changes take effect on old boxes too.
|
| 161 |
-
|
| 162 |
return ws
|
| 163 |
|
| 164 |
|
| 165 |
-
def
|
| 166 |
"""Update the metadata of the given workspace object.
|
| 167 |
|
| 168 |
The metadata is the information about the operations that the nodes in the workspace represent,
|
|
@@ -175,22 +175,36 @@ def _update_metadata(ws: Workspace) -> Workspace:
|
|
| 175 |
Returns:
|
| 176 |
Workspace: The updated workspace object.
|
| 177 |
"""
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
data = node.data
|
| 187 |
-
op = catalog.get(data.title)
|
| 188 |
-
if op:
|
| 189 |
data.meta = op
|
|
|
|
|
|
|
|
|
|
| 190 |
node.type = op.type
|
| 191 |
-
if
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
data.error =
|
| 195 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
return ws
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
j = f.read()
|
| 159 |
ws = Workspace.model_validate_json(j)
|
| 160 |
# Metadata is added after loading. This way code changes take effect on old boxes too.
|
| 161 |
+
update_metadata(ws)
|
| 162 |
return ws
|
| 163 |
|
| 164 |
|
| 165 |
+
def update_metadata(ws: Workspace) -> Workspace:
|
| 166 |
"""Update the metadata of the given workspace object.
|
| 167 |
|
| 168 |
The metadata is the information about the operations that the nodes in the workspace represent,
|
|
|
|
| 175 |
Returns:
|
| 176 |
Workspace: The updated workspace object.
|
| 177 |
"""
|
| 178 |
+
if ws.env not in ops.CATALOGS:
|
| 179 |
+
return ws
|
| 180 |
+
catalog = ops.CATALOGS[ws.env]
|
| 181 |
+
for node in ws.nodes:
|
| 182 |
+
data = node.data
|
| 183 |
+
op = catalog.get(data.title)
|
| 184 |
+
if op:
|
| 185 |
+
if data.meta != op:
|
|
|
|
|
|
|
|
|
|
| 186 |
data.meta = op
|
| 187 |
+
if hasattr(node, "_crdt"):
|
| 188 |
+
node._crdt["data"]["meta"] = op.model_dump()
|
| 189 |
+
if node.type != op.type:
|
| 190 |
node.type = op.type
|
| 191 |
+
if hasattr(node, "_crdt"):
|
| 192 |
+
node._crdt["type"] = op.type
|
| 193 |
+
if data.error == "Unknown operation.":
|
| 194 |
+
data.error = None
|
| 195 |
+
if hasattr(node, "_crdt"):
|
| 196 |
+
node._crdt["data"]["error"] = None
|
| 197 |
+
else:
|
| 198 |
+
data.error = "Unknown operation."
|
| 199 |
+
if hasattr(node, "_crdt"):
|
| 200 |
+
node._crdt["data"]["error"] = "Unknown operation."
|
| 201 |
return ws
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def connect_crdt(ws_pyd: Workspace, ws_crdt: pycrdt.Map):
|
| 205 |
+
ws_pyd._crdt = ws_crdt
|
| 206 |
+
with ws_crdt.doc.transaction():
|
| 207 |
+
for nc, np in zip(ws_crdt["nodes"], ws_pyd.nodes):
|
| 208 |
+
if "data" not in nc:
|
| 209 |
+
nc["data"] = pycrdt.Map()
|
| 210 |
+
np._crdt = nc
|