from __future__ import annotations from collections import defaultdict from io import StringIO from pathlib import Path from typing import Any, Optional import polars as pl from sglang.srt.debug_utils.comparator.output_types import ( InputIdsRecord, RankInfoRecord, ) from sglang.srt.debug_utils.comparator.report_sink import report_sink from sglang.srt.debug_utils.dump_loader import LOAD_FAILED, ValueWithMeta PARALLEL_INFO_KEYS: list[str] = ["sglang_parallel_info", "megatron_parallel_info"] def emit_display_records( *, df: pl.DataFrame, dump_dir: Path, label: str, tokenizer: Any, ) -> None: rank_rows: Optional[list[dict[str, Any]]] = _collect_rank_info( df, dump_dir=dump_dir ) if rank_rows is not None: report_sink.add(RankInfoRecord(label=label, rows=rank_rows)) input_ids_rows: Optional[list[dict[str, Any]]] = _collect_input_ids_and_positions( df, dump_dir=dump_dir, tokenizer=tokenizer ) if input_ids_rows is not None: report_sink.add(InputIdsRecord(label=label, rows=input_ids_rows)) def _render_polars_as_text(df: pl.DataFrame, *, title: Optional[str] = None) -> str: from rich.console import Console from rich.table import Table table = Table(title=title) for col in df.columns: table.add_column(col) for row in df.iter_rows(): table.add_row(*[str(v) for v in row]) buf = StringIO() Console(file=buf, force_terminal=False, width=200).print(table) return buf.getvalue().rstrip("\n") def _render_polars_as_rich_table( df: pl.DataFrame, *, title: Optional[str] = None ) -> Any: from rich.table import Table table = Table(title=title) for col in df.columns: table.add_column(col) for row in df.iter_rows(): table.add_row(*[str(v) for v in row]) return table def _collect_rank_info( df: pl.DataFrame, dump_dir: Path ) -> Optional[list[dict[str, Any]]]: unique_rows: pl.DataFrame = ( df.filter(pl.col("name") == "input_ids") .sort("rank") .unique(subset=["rank"], keep="first") ) if unique_rows.is_empty(): return None table_rows: list[dict[str, Any]] = [] for row in unique_rows.to_dicts(): meta: dict[str, Any] = ValueWithMeta.load(dump_dir / row["filename"]).meta row_data: dict[str, Any] = {"rank": row["rank"]} for key in PARALLEL_INFO_KEYS: _extract_parallel_info(row_data=row_data, info=meta.get(key, {})) table_rows.append(row_data) return table_rows or None def _collect_input_ids_and_positions( df: pl.DataFrame, dump_dir: Path, *, tokenizer: Any = None, ) -> Optional[list[dict[str, Any]]]: filtered: pl.DataFrame = df.filter(pl.col("name").is_in(["input_ids", "positions"])) if filtered.is_empty(): return None data_by_step_rank: dict[tuple[int, int], dict[str, Any]] = defaultdict(dict) for row in filtered.to_dicts(): key: tuple[int, int] = (row["step"], row["rank"]) item: ValueWithMeta = ValueWithMeta.load(dump_dir / row["filename"]) if item.value is not LOAD_FAILED: data_by_step_rank[key][row["name"]] = item.value table_rows: list[dict[str, Any]] = [] for (step, rank), data in sorted(data_by_step_rank.items()): ids = data.get("input_ids") pos = data.get("positions") ids_list: Optional[list[int]] = ( ids.flatten().tolist() if ids is not None else None ) row_data: dict[str, Any] = { "step": step, "rank": rank, "num_tokens": len(ids_list) if ids_list is not None else None, "input_ids": str(ids_list) if ids_list is not None else "N/A", "positions": str(pos.flatten().tolist()) if pos is not None else "N/A", } if tokenizer is not None and ids_list is not None: row_data["decoded_text"] = repr( tokenizer.decode(ids_list, skip_special_tokens=False) ) table_rows.append(row_data) return table_rows or None def _extract_parallel_info(row_data: dict[str, Any], info: dict[str, Any]) -> None: if not info or info.get("error"): return for key in sorted(info.keys()): if key.endswith("_rank"): base: str = key[:-5] size_key: str = f"{base}_size" if size_key in info: row_data[base] = f"{info[key]}/{info[size_key]}"