3333
3434System info (hostname=jensen-linux, IP=10.111.122.133)
3535├─ OS Ubuntu 24.04.1 LTS (Noble Numbat) (Linux 6.11.0-28-generic x86_64), Memory=26.7/125.5 GiB, Cores=32
36+ ├─ User info: user=ubuntu, uid=1000, gid=1000
3637├─ ✅ NVIDIA GPU NVIDIA RTX 6000 Ada Generation, driver 570.133.07, CUDA 12.8, Power=26.14/300.00 W, Memory=289/49140 MiB
3738├─ File Permissions
3839│ ├─ ✅ Dynamo workspace ($HOME/dynamo) writable
5253│ ├─ ✅ PyTorch 2.7.1+cu128, ✅torch.cuda.is_available
5354│ └─ PYTHONPATH $HOME/dynamo/components/frontend/src:$HOME/dynamo/components/planner/src:$HOME/dynamo/components/backends/vllm/src:$HOME/dynamo/components/backends/sglang/src:$HOME/dynamo/components/backends/trtllm/src:$HOME/dynamo/components/backends/llama_cpp/src:$HOME/dynamo/components/backends/mocker/src
5455├─ 🤖Framework
55- │ ├─ ✅ vllm 0.10.1.1, module=/opt/vllm/vllm/__init__.py, exec=/opt/dynamo/venv/bin/vllm
56- │ ├─ ❓ sglang -
57- │ └─ ❓ tensorrt_llm -
56+ │ ├─ ✅ vLLM: 0.10.1.1, module=/opt/vllm/vllm/__init__.py, exec=/opt/dynamo/venv/bin/vllm
57+ │ └─ ✅ Sglang: 0.3.0, module=/opt/sglang/sglang/__init__.py
5858└─ Dynamo $HOME/dynamo, SHA: a03d29066, Date: 2025-08-30 16:22:29 PDT
5959 ├─ ✅ Runtime components ai-dynamo-runtime 0.4.1
60- │ ├─ /opt/dynamo/venv/lib/python3.12/site-packages/ai_dynamo_runtime-0.4.1.dist-info created=2025-08-30 19:14:29 PDT
61- │ ├─ /opt/dynamo/venv/lib/python3.12/site-packages/ai_dynamo_runtime.pth modified=2025-08-30 19:14:29 PDT
62- │ │ └─ → $HOME/dynamo/lib/bindings/python/src
60+ │ │ /opt/dynamo/venv/lib/python3.12/site-packages/ai_dynamo_runtime-0.4.1.dist-info: created=2025-08-30 19:14:29 PDT
61+ │ │ /opt/dynamo/venv/lib/python3.12/site-packages/ai_dynamo_runtime.pth: modified=2025-08-30 19:14:29 PDT
62+ │ │ └─ →: $HOME/dynamo/lib/bindings/python/src
6363 │ ├─ ✅ dynamo._core $HOME/dynamo/lib/bindings/python/src/dynamo/_core.cpython-312-x86_64-linux-gnu.so, modified=2025-08-30 19:14:29 PDT
6464 │ ├─ ✅ dynamo.logits_processing $HOME/dynamo/lib/bindings/python/src/dynamo/logits_processing/__init__.py
6565 │ ├─ ✅ dynamo.nixl_connect $HOME/dynamo/lib/bindings/python/src/dynamo/nixl_connect/__init__.py
6666 │ ├─ ✅ dynamo.llm $HOME/dynamo/lib/bindings/python/src/dynamo/llm/__init__.py
6767 │ └─ ✅ dynamo.runtime $HOME/dynamo/lib/bindings/python/src/dynamo/runtime/__init__.py
6868 └─ ✅ Framework components ai-dynamo (via PYTHONPATH)
69+ │ /opt/dynamo/venv/lib/python3.12/site-packages/ai_dynamo-0.5.0.dist-info: created=2025-09-05 16:20:35 PDT
6970 ├─ ✅ dynamo.frontend $HOME/dynamo/components/frontend/src/dynamo/frontend/__init__.py
7071 ├─ ✅ dynamo.llama_cpp $HOME/dynamo/components/backends/llama_cpp/src/dynamo/llama_cpp/__init__.py
7172 ├─ ✅ dynamo.mocker $HOME/dynamo/components/backends/mocker/src/dynamo/mocker/__init__.py
@@ -116,7 +117,7 @@ class NodeInfo:
116117 status : NodeStatus = NodeStatus .NONE # Status indicator
117118
118119 # Additional metadata as key-value pairs
119- metadata : Dict [str , str ] = field (default_factory = dict )
120+ metadata : Dict [str , Any ] = field (default_factory = dict )
120121
121122 # Tree structure
122123 children : List ["NodeInfo" ] = field (default_factory = list )
@@ -142,7 +143,11 @@ def render(
142143
143144 # Determine the connector
144145 if not is_root :
145- connector = "└─" if is_last else "├─"
146+ # Check if this is a sub-category item
147+ if self .metadata and self .metadata .get ("part_of_previous" ):
148+ connector = "│"
149+ else :
150+ connector = "└─" if is_last else "├─"
146151 current_prefix = prefix + connector + " "
147152 else :
148153 current_prefix = ""
@@ -171,8 +176,10 @@ def render(
171176 if self .metadata :
172177 metadata_items = []
173178 for k , v in self .metadata .items ():
174- # Format all metadata consistently as "key=value"
175- metadata_items .append (f"{ k } ={ v } " )
179+ # Skip internal metadata that shouldn't be displayed
180+ if k != "part_of_previous" :
181+ # Format all metadata consistently as "key=value"
182+ metadata_items .append (f"{ k } ={ v } " )
176183
177184 if metadata_items :
178185 # Use consistent separator (comma) for all metadata
@@ -286,14 +293,14 @@ def __init__(self, hostname: Optional[str] = None, thorough_check: bool = False)
286293 # Add OS info
287294 self .add_child (OSInfo ())
288295
296+ # Add user info
297+ self .add_child (UserInfo ())
298+
289299 # Add GPU info
290300 gpu_info = GPUInfo ()
291301 # Always add GPU info so we can see errors like "nvidia-smi not found"
292302 self .add_child (gpu_info )
293303
294- # Add user info
295- self .add_child (UserInfo ())
296-
297304 # Add file permissions check
298305 self .add_child (FilePermissionsInfo (thorough_check = self .thorough_check ))
299306
@@ -1488,6 +1495,8 @@ def __init__(self):
14881495 ("tensorrt_llm" , "tensorRT LLM" ),
14891496 ]
14901497
1498+ frameworks_found = 0
1499+
14911500 for module_name , display_name in frameworks_to_check :
14921501 # Special handling for TensorRT-LLM to avoid NVML crashes
14931502 if module_name == "tensorrt_llm" :
@@ -1498,14 +1507,13 @@ def __init__(self):
14981507 f"/usr/lib/python{ python_version } /dist-packages" ,
14991508 ]
15001509
1501- found_in_system = False
15021510 for pkg_path in system_packages :
15031511 if os .path .exists (pkg_path ):
15041512 tensorrt_dirs = [
15051513 d for d in os .listdir (pkg_path ) if "tensorrt_llm" in d
15061514 ]
15071515 if tensorrt_dirs :
1508- found_in_system = True
1516+ frameworks_found += 1
15091517 # Try to get version safely
15101518 try :
15111519 result = subprocess .run (
@@ -1549,20 +1557,14 @@ def __init__(self):
15491557 self .add_child (package_info )
15501558 break
15511559
1552- if not found_in_system :
1553- package_info = PythonPackageInfo (
1554- package_name = display_name ,
1555- version = "-" ,
1556- is_framework = True ,
1557- is_installed = False ,
1558- )
1559- self .add_child (package_info )
1560+ # Don't add anything if not found in system
15601561 continue
15611562
15621563 # Regular import for other frameworks
15631564 try :
15641565 module = __import__ (module_name )
15651566 version = getattr (module , "__version__" , "installed" )
1567+ frameworks_found += 1
15661568
15671569 # Get module path
15681570 module_path = None
@@ -1585,14 +1587,18 @@ def __init__(self):
15851587 )
15861588 self .add_child (package_info )
15871589 except (ImportError , Exception ):
1588- # Framework not installed - show with "-"
1589- package_info = PythonPackageInfo (
1590- package_name = display_name ,
1591- version = "-" ,
1592- is_framework = True ,
1593- is_installed = False ,
1594- )
1595- self .add_child (package_info )
1590+ # Framework not installed - don't add it
1591+ pass
1592+
1593+ # If no frameworks found, set status to ERROR (X) and show what's missing
1594+ if frameworks_found == 0 :
1595+ self .status = NodeStatus .ERROR
1596+ # List all the frameworks that were checked but not found
1597+ missing_frameworks = []
1598+ for module_name , display_name in frameworks_to_check :
1599+ missing_frameworks .append (f"no { module_name } " )
1600+ missing_text = ", " .join (missing_frameworks )
1601+ self .desc = missing_text
15961602
15971603
15981604class PythonPackageInfo (NodeInfo ):
@@ -1646,9 +1652,22 @@ def __init__(self, pythonpath: str):
16461652 if pythonpath :
16471653 # Split by colon and replace home in each path
16481654 paths = pythonpath .split (":" )
1649- display_paths = [self ._replace_home_with_var (p ) for p in paths ]
1655+ display_paths = []
1656+ has_invalid_paths = False
1657+
1658+ for p in paths :
1659+ display_path = self ._replace_home_with_var (p )
1660+ # Check if path exists and is accessible
1661+ if not os .path .exists (p ) or not os .access (p , os .R_OK ):
1662+ display_paths .append (
1663+ f"\033 [38;5;196m{ display_path } \033 [0m"
1664+ ) # Bright red path
1665+ has_invalid_paths = True
1666+ else :
1667+ display_paths .append (display_path )
1668+
16501669 display_pythonpath = ":" .join (display_paths )
1651- status = NodeStatus .INFO
1670+ status = NodeStatus .WARNING if has_invalid_paths else NodeStatus . INFO
16521671 else :
16531672 display_pythonpath = "not set"
16541673 status = NodeStatus .WARNING # Show warning when PYTHONPATH is not set
@@ -1794,12 +1813,17 @@ def _find_dist_info(self) -> Optional[NodeInfo]:
17941813 stat = os .stat (path )
17951814 timestamp = self ._format_timestamp_pdt (stat .st_ctime )
17961815 return NodeInfo (
1797- label = display_path ,
1816+ label = f" { display_path } " ,
17981817 desc = f"created={ timestamp } " ,
17991818 status = NodeStatus .INFO ,
1819+ metadata = {"part_of_previous" : True },
18001820 )
18011821 except Exception :
1802- return NodeInfo (label = display_path , status = NodeStatus .INFO )
1822+ return NodeInfo (
1823+ label = f" { display_path } " ,
1824+ status = NodeStatus .INFO ,
1825+ metadata = {"part_of_previous" : True },
1826+ )
18031827 return None
18041828
18051829 def _find_pth_file (self ) -> Optional [NodeInfo ]:
@@ -1814,9 +1838,10 @@ def _find_pth_file(self) -> Optional[NodeInfo]:
18141838 stat = os .stat (pth_path )
18151839 timestamp = self ._format_timestamp_pdt (stat .st_mtime )
18161840 node = NodeInfo (
1817- label = display_path ,
1841+ label = f" { display_path } " ,
18181842 desc = f"modified={ timestamp } " ,
18191843 status = NodeStatus .INFO ,
1844+ metadata = {"part_of_previous" : True },
18201845 )
18211846
18221847 # Read where it points to
@@ -1873,13 +1898,18 @@ def __init__(self, workspace_dir: str, thorough_check: bool = False):
18731898 stat = os .stat (path )
18741899 timestamp = self ._format_timestamp_pdt (stat .st_ctime )
18751900 dist_node = NodeInfo (
1876- label = display_path ,
1901+ label = f" { display_path } " ,
18771902 desc = f"created={ timestamp } " ,
18781903 status = NodeStatus .INFO ,
1904+ metadata = {"part_of_previous" : True },
18791905 )
18801906 self .add_child (dist_node )
18811907 except Exception :
1882- dist_node = NodeInfo (label = display_path , status = NodeStatus .INFO )
1908+ dist_node = NodeInfo (
1909+ label = f" { display_path } " ,
1910+ status = NodeStatus .INFO ,
1911+ metadata = {"part_of_previous" : True },
1912+ )
18831913 self .add_child (dist_node )
18841914 break
18851915
0 commit comments