The query_graph API
Overview
Once the infrastructure has been defined with set_graph and, optionally, extended with annotate_graph, the query_graph API is used to retrieve nodes, edges, or graph-level attributes that match a set of filters, or to resolve the shortest path between two nodes.
A single QueryRequest is a choice of exactly one of:
attribute_query— one or more named node/edge/graph filters, evaluated independently.shortest_path_query— a source/destination pair to resolve via the graph's shortest path.
The QueryResponse mirrors this with a matching choice of attribute_query or shortest_path_query.
Attribute Query Requests
Query.Request.Filter accepts arrays of node_filters and edge_filters, plus a single graph_filter:
query = QueryRequest()
query.attribute_query.node_filters.add(name="...") # 0 or more
query.attribute_query.edge_filters.add(name="...") # 0 or more
query.attribute_query.graph_filter.attributes.add(attribute="...", value="...")
Every node/edge filter requires a unique name. Names are what let you tell the results of one filter apart from another when a request carries several — the response groups matches by that name rather than merging everything into one list.
Node and Edge Matching Rules
Each node_filter combines node_identifiers (or endpoints for edges) with attribute_filters. Both are optional, but at least one must be set:
node_identifiers / endpoints |
attribute_filters |
Result |
|---|---|---|
| unset | unset | No results. |
| set | unset | All matching nodes/edges, with every attribute. |
| unset | set | Every node/edge in the graph that matches the attribute filter. |
| set | set | Matching nodes/edges that also satisfy the attribute filter. |
node_identifiers and edge endpoints support the same slicing operator used by annotate_graph, so a single entry like server[0:2]xpu[0:3] expands to every matching node.
attribute_filters takes one or more attribute/value pairs and an optional logic (and/or, defaults to and) to combine them.
attribute_filters is a single filter shared across every entry in node_identifiers/endpoints within that filter — it cannot be set differently per identifier. If you need different attribute criteria for different node identifiers or edge endpoints, use separate node_filters/edge_filters entries (each with its own name), one per distinct attribute criteria.
Default Schema Properties
Every node and edge carries a set of attributes derived directly from the infrastructure schema, in addition to anything added later via annotate_graph. attribute_filters can match against these the same way it matches user-added annotations.
Node (component) attributes:
| Attribute | Description |
|---|---|
type |
The component type of this node (e.g. xpu, nic, cpu). |
device |
Name of the device model this component belongs to (e.g. dgx_h100). |
instance |
The parent instance path this component belongs to. |
instance_idx |
Numeric index of the parent instance. |
composed_device |
Full instance path this component was composed under. |
Edge attributes:
| Attribute | Description |
|---|---|
link |
Name of the link/interconnect connecting the two endpoints (e.g. pcie, nvlink). |
bandwidth |
Present when the link's physical bandwidth is defined in the schema, e.g. "1400 Gbps". |
latency |
Present when the link's physical latency is defined in the schema, e.g. "5 ns". |
type, device, instance, instance_idx, composed_device, and link are immutable — annotate_graph rejects attempts to overwrite them. bandwidth and latency are not immutable and may be overwritten.
Node Attribute Filter
query = QueryRequest()
node_filter = query.attribute_query.node_filters.add(name="smart_nic_filter")
node_filter.node_identifiers = ["dgx_h100"]
node_filter.attribute_filters.attributes.add(attribute="cx7_type", value="smart")
query_response = service.query_graph(query)
result = query_response.attribute_query.nodes[0]
for node in result.nodes:
print(node.name, {a.attribute: a.value for a in node.attributes})
Edge Attribute Filter
query = QueryRequest()
edge_filter = query.attribute_query.edge_filters.add(name="nvlink_filter")
edge_filter.attribute_filters.attributes.add(attribute="link_type", value="nvlink")
query_response = service.query_graph(query)
result = query_response.attribute_query.edges[0]
for edge in result.edges:
print(edge.ep1, edge.ep2, {a.attribute: a.value for a in edge.attributes})
Graph Attribute Filter
query = QueryRequest()
query.attribute_query.graph_filter.attributes.add(attribute="region", value="us-east")
query_response = service.query_graph(query)
graph_attrs = {a.attribute: a.value for a in query_response.attribute_query.graph}
Multiple Filters in One Request
Because node_filters/edge_filters are arrays of named filters, a single request can ask for several independent things at once, and the response keeps each one's matches separate:
query = QueryRequest()
xpus = query.attribute_query.node_filters.add(name="xpus")
xpus.attribute_filters.attributes.add(attribute="type", value="xpu")
nics = query.attribute_query.node_filters.add(name="smart_nics")
nics.attribute_filters.attributes.add(attribute="cx7_type", value="smart")
query_response = service.query_graph(query)
for result in query_response.attribute_query.nodes:
print(result.name, len(result.nodes))
Shortest Path Requests
query = QueryRequest()
query.shortest_path_query.name = "rank0-rank1"
query.shortest_path_query.source = service.get_endpoints("rank", "0")[0]
query.shortest_path_query.destination = service.get_endpoints("rank", "1")[0]
query_response = service.query_graph(query)
path = [node.name for node in query_response.shortest_path_query.nodes]
source and destination must be exact node IDs (not slice expressions) already present in the graph.
Full Examples
Node, edge, and graph attribute filter queries
import json
import pytest
import yaml
import uuid
import networkx
from infragraph import *
from datetime import datetime
from infragraph.blueprints.fabrics.clos_fat_tree_fabric import ClosFatTreeFabric
from infragraph.blueprints.fabrics.single_tier_fabric import SingleTierFabric
from infragraph.blueprints.devices.nvidia.dgx import NvidiaDGX
from infragraph.blueprints.devices.generic.generic_switch import Switch
from infragraph.blueprints.fabrics.closfabric import ClosFabric
from infragraph.infragraph_service import InfraGraphService
from infragraph.visualizer.visualize import run_visualizer
def print_graph(service):
g = service.get_networkx_graph()
# validations
print("\nAnnotated node attributes:")
for node, data in g.nodes(data=True):
print(f" {node}: {data}")
print("\nAnnotated edge attributes:")
for u, v, data in g.edges(data=True):
print(f" {u} -- {v}: {data}")
print("\nAnnotated graph attributes:")
print(f" {g.graph}")
def visualize_dgx(infrastructure, annotation, output=None):
"""Build the annotated DGX infrastructure and launch the visualizer on it.
A fresh output folder is generated on every call so repeated runs never
clobber a prior visualization.
"""
if output is None:
stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output = f"./viz_dgx_{stamp}_{uuid.uuid4().hex[:6]}"
run_visualizer(infrastructure=infrastructure, annotations=annotation, output=output)
def _annotate_graph(service, **kwargs):
annotation = Annotation()
for attr, value in kwargs.items():
annotation.graph.add(attribute=attr, value=str(value))
service.annotate_graph(annotation)
return annotation
def _annotate_node(service, node_name, **kwargs):
annotation = Annotation()
node_annotation = annotation.nodes.add(name=node_name)
for attr, value in kwargs.items():
node_annotation.attributes.add(attribute=attr, value=str(value))
service.annotate_graph(annotation)
return annotation
def _annotate_edge(service, ep1, ep2, **kwargs):
annotation = Annotation()
edge_annotation = annotation.edges.add(ep1=ep1, ep2=ep2)
for attr, value in kwargs.items():
edge_annotation.attributes.add(attribute=attr, value=str(value))
service.annotate_graph(annotation)
return annotation
def _annotate_topology(service):
_annotate_graph(service, fabric="Single Tier Topology", hosts=["dgx_h100[0]", "dgx_h100[1]"], region="us-east")
# annotate nodes here - the devices
_annotate_node(service, node_name="dgx_h100[0]", location="rack 0")
_annotate_node(service, node_name="dgx_h100[1]", location="rack 8")
# switch asic name
_annotate_node(service, node_name="switch[0]asic[0]", vendor="intel tofino")
# add device type?
_annotate_node(service, node_name="switch", device_type="switch")
_annotate_node(service, node_name="dgx_h100", device_type="host")
# set ranks?
for i in range(0, 16):
dev_index = 0 if i < 8 else 1
comp_index = i % 8
_annotate_node(service, node_name=f"dgx_h100[{str(dev_index)}]xpu[{str(comp_index)}]", rank=str(i))
# set dgx 0 cx7 annotation to smart cx7
_annotate_node(service, node_name="dgx_h100[0]cx7", cx7_type="smart cx7")
# set dgx 1 cpu annotation to hyperthreaded
_annotate_node(service, node_name="dgx_h100[1]cpu", cpu_type="hyper threaded RISC")
# add annotation of nvlink to both edges
_annotate_edge(service, ep1="dgx_h100[0]xpu", ep2="dgx_h100[0]nvsw", latency="0.01", link_type="nvlink", error_rate="6")
_annotate_edge(service, ep1="dgx_h100[1]xpu", ep2="dgx_h100[1]nvsw", latency="0.08", link_type="nvlink", error_rate="20")
@pytest.fixture
def service():
dgx = NvidiaDGX()
clos_fat_tree = SingleTierFabric(dgx, 2)
svc = InfraGraphService()
svc.set_graph(clos_fat_tree)
_annotate_topology(svc)
return svc
def test_node_filter_attribute_query(service):
query = QueryRequest()
node_filter = query.attribute_query.node_filters.add(name="cpu_type_filter")
node_filter.attribute_filters.attributes.add(attribute="cpu_type", value="hyper threaded")
query_response = service.query_graph(query).attribute_query
nodes = query_response.nodes[0].nodes
assert query_response.nodes[0].name == "cpu_type_filter"
assert len(nodes) == 2
assert "dgx_h100.1.cpu." in nodes[0].name
assert len(query_response.edges) == 0
assert len(query_response.graph) == 0
def test_query_node_attribute(service):
# get all smart nics
query = QueryRequest()
node_filter = query.attribute_query.node_filters.add(name="cx7_type_filter")
node_filter.node_identifiers = ["dgx_h100"]
node_filter.attribute_filters.attributes.add(attribute="cx7_type", value="smart")
query_response = service.query_graph(query).attribute_query
nodes = query_response.nodes[0].nodes
assert len(nodes) == 8
assert "dgx_h100" in nodes[0].name
assert len(query_response.edges) == 0
assert len(query_response.graph) == 0
def test_query_rank_node_attribute(service):
# get all smart nics
query = QueryRequest()
node_filter = query.attribute_query.node_filters.add(name="rank_filter")
node_filter.attribute_filters.attributes.add(attribute="rank", value="")
query_response = service.query_graph(query).attribute_query
nodes = query_response.nodes[0].nodes
assert len(nodes) == 16
assert "xpu" in nodes[0].name
assert len(query_response.edges) == 0
assert len(query_response.graph) == 0
# print_graph(service)
# visualize_dgx(service.infrastructure, None, "clos_visual")
def test_query_nic_node_attribute(service):
# get all smart nics
query = QueryRequest()
node_filter = query.attribute_query.node_filters.add(name="nic_type_filter")
node_filter.attribute_filters.attributes.add(attribute="type", value="nic")
query_response = service.query_graph(query).attribute_query
nodes = query_response.nodes[0].nodes
assert len(nodes) == 16
assert "cx7" in nodes[0].name
assert len(query_response.edges) == 0
assert len(query_response.graph) == 0
# get specific smart nics
query = QueryRequest()
node_filter = query.attribute_query.node_filters.add(name="nic_type_filter")
node_filter.node_identifiers = ["dgx_h100[1]"]
node_filter.attribute_filters.attributes.add(attribute="type", value="nic")
query_response = service.query_graph(query).attribute_query
nodes = query_response.nodes[0].nodes
assert len(nodes) == 8
assert "cx7" in nodes[0].name
assert len(query_response.edges) == 0
assert len(query_response.graph) == 0
# print_graph(service)
# visualize_dgx(service.infrastructure, None, "clos_visual")
def test_edge_filter_attribute_query(service):
query = QueryRequest()
edge_filter = query.attribute_query.edge_filters.add(name="nvlink_filter")
edge_filter.attribute_filters.attributes.add(attribute="link_type", value="nvlink")
query_response = service.query_graph(query).attribute_query
edges = query_response.edges[0].edges
assert len(edges) == 64
attrs = {a.attribute: a.value for a in edges[0].attributes}
assert attrs["link_type"] == "nvlink"
assert len(query_response.nodes) == 0
assert len(query_response.graph) == 0
def test_query_graph_attribute(service):
# get all smart nics
query = QueryRequest()
query.attribute_query.graph_filter.attributes.add(attribute="region", value="us-east")
query_response = service.query_graph(query).attribute_query
assert len(query_response.graph) > 0
assert len(query_response.edges) == 0
assert len(query_response.nodes) == 0
if __name__ == "__main__":
pytest.main(["-s", __file__])
Shortest path query
from typing import Tuple
import pytest
from infragraph import *
from infragraph.blueprints.fabrics.closfabric import ClosFabric
from infragraph.infragraph_service import InfraGraphService
@pytest.mark.asyncio
@pytest.mark.parametrize("ranks", [(i, i + 1) for i in range(0, 7)])
async def test_shortest_path(ranks: Tuple[int, int]):
"""Test resolving the shortest path from one rank to another"""
service = InfraGraphService()
service.set_graph(ClosFabric().serialize())
# add ranks
npu_endpoints = service.get_endpoints("type", Component.XPU)
annotation = Annotation()
for idx, npu_endpoint in enumerate(npu_endpoints):
annotation_node = annotation.nodes.add(
name=npu_endpoint
)
annotation_node.attributes.add(attribute="rank", value=str(idx))
service.annotate_graph(annotation.serialize())
# find shortest path from one rank to another
query = QueryRequest()
query.shortest_path_query.name = "rank0-rank1"
query.shortest_path_query.source = service.get_endpoints("rank", str(ranks[0]))[0]
query.shortest_path_query.destination = service.get_endpoints("rank", str(ranks[1]))[0]
query_response = service.query_graph(query)
shortest_route = ""
for node in query_response.shortest_path_query.nodes:
shortest_route = shortest_route + " -> " + node.name
print(f"\nShortest Path between rank {ranks[0]} and rank {ranks[1]}")
print(shortest_route)
if __name__ == "__main__":
pytest.main(["-s", __file__])