Browse Source

Add left chat panel to the viz

pull/2507/head
Gagan Bansal 2 years ago
parent
commit
240e15d38f
2 changed files with 112 additions and 35 deletions
  1. +12
    -8
      samples/tools/profiler/profiler/visualize.py
  2. +100
    -27
      samples/tools/profiler/profiler/viz/d3_dag.html

+ 12
- 8
samples/tools/profiler/profiler/visualize.py View File

@@ -16,20 +16,21 @@ class ProfileVisualizer(Protocol):


class Node:
def __init__(self, name):
def __init__(self, name, profile):
self.name = name
self.children = []
self.profile = profile


class DAG:
def __init__(self):
self.nodes = {}

def add_edge(self, source_name, dest_name):
def add_edge(self, source_name, dest_name, source_profile=None, dest_profile=None):
if source_name not in self.nodes:
self.nodes[source_name] = Node(source_name)
self.nodes[source_name] = Node(source_name, source_profile)
if dest_name not in self.nodes:
self.nodes[dest_name] = Node(dest_name)
self.nodes[dest_name] = Node(dest_name, dest_profile)
self.nodes[source_name].children.append(self.nodes[dest_name])

def to_json(self):
@@ -37,7 +38,10 @@ class DAG:
node_index = {}

for node_name, node in self.nodes.items():
graph["nodes"].append({"name": node_name})
# Convert the profile to a dictionary
profile_dict = node.profile.to_dict() if node.profile else None

graph["nodes"].append({"name": node_name, "profile": profile_dict})
node_index[node_name] = len(graph["nodes"]) - 1

for node_name, node in self.nodes.items():
@@ -69,7 +73,7 @@ class DAGVisualizer:
sorted_next_states = sorted([s.name for s in next.states])
current_states = f"[{i}]:\n" + "\n".join(sorted_current_states)
next_states = f"[{i+1}]:\n" + "\n".join(sorted_next_states)
dag.add_edge(current_states, next_states)
dag.add_edge(current_states, next_states, current, next)

return dag

@@ -98,7 +102,7 @@ class DAGVisualizer:

index_html_path = pkg_resources.resource_filename(__name__, os.path.join("viz", "d3_dag.html"))

dag_json = json.dumps(dag.to_json())
dag_json = json.dumps(dag.to_json(), indent=2)

if filename is None:
raise ValueError("Output filename must be provided.")
@@ -108,7 +112,7 @@ class DAGVisualizer:
with open(index_html_path, "r") as index_html:
html_content = index_html.read()
# Replace the placeholder with the actual dag_json data
html_content = html_content.replace("__DAG_DATA__", dag_json)
html_content = html_content.replace('"__DAG_DATA__"', dag_json)
f.write(html_content)

print(f"Visualization saved to {filename}")

+ 100
- 27
samples/tools/profiler/profiler/viz/d3_dag.html View File

@@ -6,21 +6,52 @@
<title>DAG Visualization</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
html, body {
height: 100%; /* Full height for the html and body */
margin: 0; /* Remove default margin */
padding: 0; /* Remove default padding */
display: flex; /* Enable flexbox */
justify-content: center; /* Center horizontally */
align-items: center; /* Center vertically */
background-color: #f4f4f4; /* Background color */
box-sizing: border-box; /* Border and padding included in width/height */
}
#container {
display: flex; /* Flex container for the panels */
width: 90vw; /* 90% of viewport width */
height: 80vh; /* 80% of viewport height */
padding: 20px; /* Padding around the container */
box-sizing: border-box; /* Include padding in the width and height calculation */
}
#left-panel {
width: 40%;
height: 100%; /* Full height of the container */
overflow-y: auto;
padding: 10px;
border: 1px solid gray;
border-radius: 10px;
font-size: 10px;
box-sizing: border-box;
direction: rtl; /* Right to Left text direction for scrollbar */
}
#left-panel > * {
direction: ltr; /* Left to Right text direction for contents */
}
#svg-container {
border: 1px solid black;
flex: 1; /* Take remaining space */
height: 100%; /* Full height of the container */
margin-left: 2%; /* Gutter between panels */
border: solid 1px black;
border-radius: 10px;
box-sizing: border-box;
background-color: white; /* Ensure background is white for visibility */
}
#svg {
width: 100%;
height: 100%;
}

text {
font-size: 14px; /* Increased from 10px to 14px */
font-size: 14px;
text-anchor: middle;
pointer-events: none;
fill: #333;
@@ -37,27 +68,44 @@
stroke: #333;
stroke-width: 1.5px;
}
.node-info {
margin-bottom: 10px;
padding: 10px;
cursor: pointer;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
}
.node-info:hover {
background-color: #eee;
}
.selected-message {
outline: 2px solid blue;
}
</style>

</head>
<body>
<svg id="svg-container">
<!-- Define arrowhead -->
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="12" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#999"></path>
</marker>
</defs>
</svg>

<div id="container">
<div id="left-panel">
<!-- Node information will be populated here -->
</div>
<div id="svg-container">
<svg id="svg"></svg>
</div>
</div>

<script>
let dag_data = String.raw`__DAG_DATA__`;
let data = JSON.parse(dag_data);
let data = "__DAG_DATA__";

const svg = d3.select("#svg");
const svgContainer = d3.select("#svg-container");
const width = window.innerWidth * 0.8; // 80% width
const height = window.innerHeight * 0.8; // 80% height
const svg = svgContainer.attr("width", width).attr("height", height);
const width = svgContainer.node().getBoundingClientRect().width;
const height = svgContainer.node().getBoundingClientRect().height;

const zoom = d3.zoom()
.scaleExtent([0.1, 10])
.on("zoom", (event) => {
container.attr("transform", event.transform);
});
@@ -72,7 +120,7 @@
.attr("class", "link")
.attr("marker-end", "url(#arrow)");

const nodeRadius = 30; // Increased from 20 to 30
const nodeRadius = 30;
const simulation = d3.forceSimulation(data.nodes)
.force("link", d3.forceLink(data.links).id(d => d.index).distance(100))
.force("charge", d3.forceManyBody().strength(-300))
@@ -86,14 +134,31 @@
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
.on("end", dragended))
.on("click", (event, d) => {
const index = d.index;
const nodeInfoElement = document.getElementById(`node-info-${index}`);
if (nodeInfoElement) {
// Scroll to the corresponding message
nodeInfoElement.scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest" });

// Remove outline from previously selected message
const previousSelected = document.querySelector(".selected-message");
if (previousSelected) {
previousSelected.classList.remove("selected-message");
}

// Add outline to the selected message
nodeInfoElement.classList.add("selected-message");
}
});

node.append("circle")
.attr("r", nodeRadius);

node.append("text")
.attr("dy", "0.35em")
.text(d => d.name);
.text(d => d.profile.message.source);

simulation.on("tick", () => {
link.attr("x1", d => d.source.x)
@@ -104,6 +169,15 @@
node.attr("transform", d => `translate(${d.x},${d.y})`);
});

// Populate left panel with node information
const leftPanel = d3.select("#left-panel");
const nodeInfo = leftPanel.selectAll(".node-info")
.data(data.nodes)
.enter().append("div")
.attr("class", "node-info")
.attr("id", (d, i) => `node-info-${i}`) // Add unique IDs to each node info
.text(d => `${d.profile.message.source}: ${d.profile.message.content}`);

function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
@@ -120,7 +194,6 @@
d.fx = null;
d.fy = null;
}

</script>
</body>
</html>

Loading…
Cancel
Save