Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/server/package/src/model_explorer/web_app/index.html

Large diffs are not rendered by default.

Large diffs are not rendered by default.

3,158 changes: 3,158 additions & 0 deletions src/server/package/src/model_explorer/web_app/worker-HZKU6U4Y.js

Large diffs are not rendered by default.

3,158 changes: 0 additions & 3,158 deletions src/server/package/src/model_explorer/web_app/worker-SHX7LTLI.js

This file was deleted.

252 changes: 195 additions & 57 deletions src/ui/src/components/visualizer/worker/graph_processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {getLayoutGraph} from './graph_layout';
import {updateProcessingProgress} from './utils';

const CONST_VALUE_REGEX = /dense<([^>]*)>/;
const CONST_EVAL_LAYER_NAME = 'const_eval';

/**
* A class that processes a given `Graph` into a `ModelGraph`.
Expand Down Expand Up @@ -99,6 +100,10 @@ export class GraphProcessor {
ProcessingLabel.PROCESSING_LAYER_NAMESPACES,
);

if (this.flattenLayers) {
Comment thread
dcblundell marked this conversation as resolved.
this.addSeparateConstEvalLayer(modelGraph);
}

this.generateLayoutGraphConnections(modelGraph);
updateProcessingProgress(
this.paneId,
Expand Down Expand Up @@ -353,63 +358,7 @@ export class GraphProcessor {
}
}

// Find group nodes that only have one single op node as its child. For
// these nodes, remove the group node and move the child op node up a level
// from its namespace.
//
// Repeatedly do this until no such nodes are found.
if (!this.keepLayersWithASingleChild) {
while (true) {
let numNodeProcessed = 0;
for (const node of modelGraph.nodes) {
if (!isGroupNode(node)) {
continue;
}
if (node.nsChildrenIds != null && node.nsChildrenIds.length === 1) {
const opNode = modelGraph.nodesById[node.nsChildrenIds[0]];
if (isOpNode(opNode)) {
numNodeProcessed++;
// Delete group node.
const index = modelGraph.nodes.indexOf(node);
if (index >= 0) {
modelGraph.nodes.splice(index, 1);
}
delete modelGraph.nodesById[node.id];

// Move op node up one level in namespace.
const ns = opNode.namespace;
const parts = this.getNonEmptyNamespaceComponents(ns);
parts.pop();
opNode.namespace = parts.join('/');
opNode.savedNamespace = opNode.namespace;
opNode.level = parts.length;
opNode.nsParentId = node.nsParentId;

// Update root node if necessary.
const indexInRootNodes = modelGraph.rootNodes.indexOf(node);
if (indexInRootNodes >= 0) {
modelGraph.rootNodes.splice(indexInRootNodes, 1);
modelGraph.rootNodes.push(opNode);
}

// Remove this node from its NS parent node's nsChildrenIds, and add
// the op node to it.
if (node.nsParentId) {
const nsParent = modelGraph.nodesById[
node.nsParentId
] as GroupNode;
const index = nsParent.nsChildrenIds!.indexOf(node.id);
nsParent.nsChildrenIds!.splice(index, 1);
nsParent.nsChildrenIds!.push(opNode.id);
}
}
}
}
if (numNodeProcessed === 0) {
break;
}
}
}
this.removeSingleChildGroupNodes(modelGraph);
}

/**
Expand Down Expand Up @@ -522,6 +471,195 @@ export class GraphProcessor {
}
}

/**
* Finds all nodes with "const_eval" in their namespace and reorganizes them
* under a separate "const_eval" root group, preserving their hierarchy.
*/
addSeparateConstEvalLayer(modelGraph: ModelGraph) {
const constEvalNodes: OpNode[] = [];

// Get all nodes that have "const_eval" in their namespace
Comment thread
dcblundell marked this conversation as resolved.
Outdated
for (const node of modelGraph.nodes) {
if (isOpNode(node) && node.fullNamespace?.includes(CONST_EVAL_LAYER_NAME)) {
Comment thread
dcblundell marked this conversation as resolved.
Outdated
constEvalNodes.push(node);
}
}

if (constEvalNodes.length === 0) {
return;
}

// Create the const_eval root group node
const constEvalGroupId = `${CONST_EVAL_LAYER_NAME}___group___`;
const constEvalGroupNode: GroupNode = {
nodeType: NodeType.GROUP_NODE,
id: constEvalGroupId,
namespace: '',
label: CONST_EVAL_LAYER_NAME,
level: 0,
expanded: false,
nsChildrenIds: [],
};

modelGraph.nodes.push(constEvalGroupNode);
modelGraph.nodesById[constEvalGroupId] = constEvalGroupNode;

const seenNamespaces = new Set<string>();

// Remove nodes from old parents and update their namespaces
for (const node of constEvalNodes) {
this.removeFromParentOrRoot(modelGraph, node);

// Restore full namespace hierarchy
node.namespace = node.fullNamespace || node.namespace;

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When restoring the namespace hierarchy, consider updating savedNamespace to match the restored namespace. Looking at line 625 in removeSingleChildGroupNodes, when node namespaces are updated, savedNamespace is also updated. For consistency, add:
node.savedNamespace = node.namespace;
after line 495.

Suggested change
node.namespace = node.fullNamespace || node.namespace;
node.namespace = node.fullNamespace || node.namespace;
node.savedNamespace = node.namespace;

Copilot uses AI. Check for mistakes.
node.level = this.getNonEmptyNamespaceComponents(node.namespace).length;
node.nsParentId = undefined;

// Create ancestor group nodes (same pattern as processNodes)
const ancestorNamespaces = this.getAncestorNamespaces(node.namespace);
for (const ns of ancestorNamespaces) {
if (seenNamespaces.has(ns)) {
continue;
}
seenNamespaces.add(ns);

const groupId = this.getGroupNodeIdFromNamespace(ns);
if (modelGraph.nodesById[groupId]) {
continue;
}

const components = ns.split('/');
const label = components.splice(-1)[0];
const namespace = components.join('/');
const groupNode: GroupNode = {
nodeType: NodeType.GROUP_NODE,
id: groupId,
namespace,
label,
level: components.length,
expanded: false,
};

modelGraph.nodes.push(groupNode);
modelGraph.nodesById[groupNode.id] = groupNode;
Comment thread
dcblundell marked this conversation as resolved.
Outdated
}
Comment on lines +499 to +502

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a node has namespace exactly equal to "const_eval", then getAncestorNamespaces will return ["const_eval"], and the loop at lines 501-503 will call createGroupNodeForNamespace with namespace "const_eval". This will try to create a group node with ID "const_eval___group___", which is the same ID as the root const_eval group node created at line 474. This causes a duplicate node in modelGraph.nodes and overwrites the original node in modelGraph.nodesById.

To fix this, add "const_eval" to seenNamespaces before the loop:
seenNamespaces.add(CONST_EVAL_LAYER_NAME);
after line 488.

Copilot uses AI. Check for mistakes.
}

// Rebuild parent-child relationships (same pattern as processNamespaceRelationships)
const nodesToLink = [
constEvalGroupNode,
...Array.from(seenNamespaces).map(ns => modelGraph.nodesById[this.getGroupNodeIdFromNamespace(ns)]),

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a filter to handle any potential undefined nodes. While createGroupNodeForNamespace should create all nodes in seenNamespaces, adding a safety check would make the code more robust:
.filter(node => node != null) after the map operation.

Suggested change
...Array.from(seenNamespaces).map(ns => modelGraph.nodesById[this.getGroupNodeIdFromNamespace(ns)]),
...Array.from(seenNamespaces)
.map(ns => modelGraph.nodesById[this.getGroupNodeIdFromNamespace(ns)])
.filter(node => node != null),

Copilot uses AI. Check for mistakes.
...constEvalNodes,
];

for (const node of nodesToLink) {
const ns = node.namespace;

if (ns === '') {
modelGraph.rootNodes.push(node);
continue;
}

const parentNodeId = this.getGroupNodeIdFromNamespace(ns);
const parentGroupNode = modelGraph.nodesById[parentNodeId] as GroupNode;

if (parentGroupNode) {
node.nsParentId = parentGroupNode.id;

if (!parentGroupNode.nsChildrenIds) {
parentGroupNode.nsChildrenIds = [];
}
if (!parentGroupNode.nsChildrenIds.includes(node.id)) {
parentGroupNode.nsChildrenIds.push(node.id);
}
}
}

this.removeSingleChildGroupNodes(modelGraph);
Comment thread
dcblundell marked this conversation as resolved.
Outdated
}

/**
* Removes a node from its parent's children list or from root nodes.
*/
private removeFromParentOrRoot(modelGraph: ModelGraph, node: ModelNode) {
if (node.nsParentId) {
const parent = modelGraph.nodesById[node.nsParentId] as GroupNode;
if (parent?.nsChildrenIds) {
const index = parent.nsChildrenIds.indexOf(node.id);
if (index >= 0) {
parent.nsChildrenIds.splice(index, 1);
}
}
} else {
const index = modelGraph.rootNodes.indexOf(node);
if (index >= 0) {
modelGraph.rootNodes.splice(index, 1);
}
}
}

/**
* Find group nodes that only have one single op node as its child.
* For these nodes, remove the group node and move the child op node up a level.
* Repeatedly do this until no such nodes are found.
*/
private removeSingleChildGroupNodes(modelGraph: ModelGraph) {
if (this.keepLayersWithASingleChild) {
return;
}

while (true) {
let numNodeProcessed = 0;
for (const node of modelGraph.nodes) {
if (!isGroupNode(node)) {
continue;
}
if (node.nsChildrenIds != null && node.nsChildrenIds.length === 1) {
const opNode = modelGraph.nodesById[node.nsChildrenIds[0]];
if (isOpNode(opNode)) {
numNodeProcessed++;
// Delete group node.
const index = modelGraph.nodes.indexOf(node);
if (index >= 0) {
modelGraph.nodes.splice(index, 1);
}
delete modelGraph.nodesById[node.id];

// Move op node up one level in namespace.
const ns = opNode.namespace;
const parts = this.getNonEmptyNamespaceComponents(ns);
parts.pop();
opNode.namespace = parts.join('/');
opNode.savedNamespace = opNode.namespace;
opNode.level = parts.length;
opNode.nsParentId = node.nsParentId;

// Update root node if necessary.
const indexInRootNodes = modelGraph.rootNodes.indexOf(node);
if (indexInRootNodes >= 0) {
modelGraph.rootNodes.splice(indexInRootNodes, 1);
modelGraph.rootNodes.push(opNode);
}

// Remove this node from its NS parent node's nsChildrenIds, and add
// the op node to it.
if (node.nsParentId) {
const nsParent = modelGraph.nodesById[
node.nsParentId
] as GroupNode;
const index = nsParent.nsChildrenIds!.indexOf(node.id);
nsParent.nsChildrenIds!.splice(index, 1);
nsParent.nsChildrenIds!.push(opNode.id);
}
}
}
}
if (numNodeProcessed === 0) {
break;
}
}
}

/**
* Finds group nodes with a large number of children, and splits them into
* different groups
Expand Down