Add sglang sglang request perf gate - #11
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new performance gating script, tools/request_perf_gate.py, which compares baseline and current performance metrics to detect regressions. The reviewer provided valuable feedback to enhance the script's robustness and flexibility, including implementing defensive parsing for JSON and JSON Lines (NDJSON) formats, parameterizing the compare function, expanding the self-test coverage to include regression and missing-case scenarios, and adding command-line arguments for custom metrics and tolerance thresholds.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| def load(path: Path) -> dict[str, float]: | ||
| data = json.loads(path.read_text(encoding="utf-8")) | ||
| if isinstance(data, list): | ||
| return {str(item["name"]): float(item[METRIC]) for item in data} | ||
| return {str(k): float(v[METRIC] if isinstance(v, dict) else v) for k, v in data.items()} |
There was a problem hiding this comment.
The current load function is fragile and will crash with KeyError, TypeError, or ValueError if the JSON structure is slightly different than expected (e.g., missing keys, non-numeric values, or unexpected types). Additionally, since benchmark tools in SGLang (like bench_offline_throughput.py) append results to a file, the output file can be in JSON Lines (NDJSON) format, which json.loads() on the entire file content will fail to parse.
To make the performance gate robust and prevent CI/CD pipeline failures, we should implement defensive parsing that handles both standard JSON and JSON Lines, safely retrieves keys with fallbacks, and ignores malformed entries gracefully.
def load(path: Path, metric: str = METRIC) -> dict[str, float]:
try:
content = path.read_text(encoding="utf-8").strip()
except Exception as e:
raise RuntimeError(f"Failed to read file {path}: {e}")
if not content:
return {}
try:
data = json.loads(content)
except json.JSONDecodeError:
try:
data = [json.loads(line) for line in content.splitlines() if line.strip()]
except json.JSONDecodeError as e:
raise ValueError(f"Failed to parse JSON from {path}: {e}")
result = {}
if isinstance(data, list):
for idx, item in enumerate(data):
if not isinstance(item, dict):
continue
name = item.get("name") or item.get("model") or f"case_{idx}"
val = item.get(metric)
if val is not None:
try:
result[str(name)] = float(val)
except (ValueError, TypeError):
pass
elif isinstance(data, dict):
for k, v in data.items():
if isinstance(v, dict):
val = v.get(metric)
else:
val = v
if val is not None:
try:
result[str(k)] = float(val)
except (ValueError, TypeError):
pass
return result| def compare(baseline: dict[str, float], current: dict[str, float]) -> dict[str, object]: | ||
| rows: list[dict[str, object]] = [] | ||
| failed = False | ||
| for name, old in sorted(baseline.items()): | ||
| if name not in current: | ||
| rows.append({"name": name, "status": "missing-current"}) | ||
| failed = True | ||
| continue | ||
| new = current[name] | ||
| ratio = (new - old) / old if old else 0.0 | ||
| status = "regression" if ratio < -TOLERANCE else "ok" | ||
| failed = failed or status != "ok" | ||
| rows.append({"name": name, "baseline": old, "current": new, "delta_ratio": ratio, "status": status}) | ||
| return {"ok": not failed, "metric": METRIC, "rows": rows} |
There was a problem hiding this comment.
To support configurable metrics and tolerances via the CLI, update the compare function to accept metric and tolerance as parameters instead of relying on hardcoded global constants.
| def compare(baseline: dict[str, float], current: dict[str, float]) -> dict[str, object]: | |
| rows: list[dict[str, object]] = [] | |
| failed = False | |
| for name, old in sorted(baseline.items()): | |
| if name not in current: | |
| rows.append({"name": name, "status": "missing-current"}) | |
| failed = True | |
| continue | |
| new = current[name] | |
| ratio = (new - old) / old if old else 0.0 | |
| status = "regression" if ratio < -TOLERANCE else "ok" | |
| failed = failed or status != "ok" | |
| rows.append({"name": name, "baseline": old, "current": new, "delta_ratio": ratio, "status": status}) | |
| return {"ok": not failed, "metric": METRIC, "rows": rows} | |
| def compare( | |
| baseline: dict[str, float], | |
| current: dict[str, float], | |
| metric: str = METRIC, | |
| tolerance: float = TOLERANCE, | |
| ) -> dict[str, object]: | |
| rows: list[dict[str, object]] = [] | |
| failed = False | |
| for name, old in sorted(baseline.items()): | |
| if name not in current: | |
| rows.append({"name": name, "status": "missing-current"}) | |
| failed = True | |
| continue | |
| new = current[name] | |
| ratio = (new - old) / old if old else 0.0 | |
| status = "regression" if ratio < -tolerance else "ok" | |
| failed = failed or status != "ok" | |
| rows.append({"name": name, "baseline": old, "current": new, "delta_ratio": ratio, "status": status}) | |
| return {"ok": not failed, "metric": metric, "rows": rows} |
| def self_test() -> None: | ||
| data = compare({"case": 100.0}, {"case": 99.0}) | ||
| assert data["ok"] | ||
| print(json.dumps({"ok": True, "rows": len(data["rows"])}, ensure_ascii=False)) |
There was a problem hiding this comment.
Expand the self_test function to cover regression and missing-case scenarios, ensuring that the regression detection logic is thoroughly validated.
| def self_test() -> None: | |
| data = compare({"case": 100.0}, {"case": 99.0}) | |
| assert data["ok"] | |
| print(json.dumps({"ok": True, "rows": len(data["rows"])}, ensure_ascii=False)) | |
| def self_test() -> None: | |
| # Test OK case | |
| data_ok = compare({"case": 100.0}, {"case": 99.0}) | |
| assert data_ok["ok"] | |
| # Test regression case | |
| data_reg = compare({"case": 100.0}, {"case": 90.0}) | |
| assert not data_reg["ok"] | |
| assert data_reg["rows"][0]["status"] == "regression" | |
| # Test missing case | |
| data_missing = compare({"case": 100.0}, {}) | |
| assert not data_missing["ok"] | |
| assert data_missing["rows"][0]["status"] == "missing-current" | |
| print(json.dumps({"ok": True, "rows": len(data_ok["rows"])}, ensure_ascii=False)) |
| def main() -> int: | ||
| parser = argparse.ArgumentParser(description=__doc__) | ||
| parser.add_argument("baseline") | ||
| parser.add_argument("current") | ||
| parser.add_argument("--self-test", action="store_true") | ||
| args = parser.parse_args() | ||
| if args.self_test: | ||
| self_test() | ||
| return 0 | ||
| result = compare(load(Path(args.baseline)), load(Path(args.current))) | ||
| print(json.dumps(result, ensure_ascii=False, indent=2)) | ||
| return 0 if result["ok"] else 1 |
There was a problem hiding this comment.
Add --metric and --tolerance command-line arguments to allow users to customize the target metric (e.g., request_throughput, output_throughput) and the regression threshold (e.g., 0.05 for 5%) without modifying the script.
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("baseline")
parser.add_argument("current")
parser.add_argument("--metric", default=METRIC, help="Metric to compare (default: %(default)s)")
parser.add_argument("--tolerance", type=float, default=TOLERANCE, help="Regression tolerance ratio (default: %(default)s)")
parser.add_argument("--self-test", action="store_true")
args = parser.parse_args()
if args.self_test:
self_test()
return 0
metric = args.metric
tolerance = args.tolerance
result = compare(
load(Path(args.baseline), metric=metric),
load(Path(args.current), metric=metric),
metric=metric,
tolerance=tolerance,
)
print(json.dumps(result, ensure_ascii=False, indent=2))
return 0 if result["ok"] else 1- Add SGLang request performance gate - Harden SGLang request performance gate
Summary
Validation
Review notes