import time from typing import List, Optional from sglang.test.accuracy_test_runner import ( AccuracyTestParams, AccuracyTestResult, run_accuracy_test, write_accuracy_github_summary, ) from sglang.test.nightly_utils import NightlyBenchmarkRunner from sglang.test.performance_test_runner import ( PerformanceTestParams, PerformanceTestResult, run_performance_test, ) from sglang.test.test_utils import DEFAULT_URL_FOR_TEST, ModelLaunchSettings, is_in_ci from sglang.test.tool_call_test_runner import ( ToolCallTestParams, ToolCallTestResult, run_tool_call_test, ) def run_combined_tests( models: List[ModelLaunchSettings], test_name: str = "NightlyTest", base_url: Optional[str] = None, is_vlm: bool = False, accuracy_params: Optional[AccuracyTestParams] = None, performance_params: Optional[PerformanceTestParams] = None, tool_call_params: Optional[ToolCallTestParams] = None, ) -> dict: """Run performance, accuracy, and/or tool call tests for a list of models. Args: models: List of ModelLaunchSettings to test test_name: Name for the test (used in reports) base_url: Server base URL (default: DEFAULT_URL_FOR_TEST) is_vlm: Whether these are VLM models (affects defaults) accuracy_params: Parameters for accuracy tests (None to skip accuracy) performance_params: Parameters for performance tests (None to skip perf) tool_call_params: Parameters for tool call tests (None to skip tool call) Returns: dict with test results: { "all_passed": bool, "results": [ { "model": str, "perf_result": PerformanceTestResult/None, "accuracy_result": AccuracyTestResult/None, "errors": list, }, ... ] } """ base_url = base_url or DEFAULT_URL_FOR_TEST run_perf = performance_params is not None run_accuracy = accuracy_params is not None run_tool_call = tool_call_params is not None # Print test header print("\n" + "=" * 80) print(f"RUNNING: {test_name}") print(f" Models: {len(models)}") if run_accuracy: print(f" Accuracy dataset: {accuracy_params.dataset}") if run_perf: print(f" Performance batches: {performance_params.batch_sizes}") if run_tool_call: print(" Tool call tests: enabled") print("=" * 80) # Set up performance parameters if run_perf: perf = performance_params profile_dir = perf.profile_dir or ( "performance_profiles_vlms" if is_vlm else "performance_profiles_text_models" ) perf_runner = NightlyBenchmarkRunner( profile_dir=profile_dir, test_name=test_name, base_url=base_url, ) perf_runner.setup_profile_directory() else: perf_runner = None # Run tests for each model all_results = [] all_passed = True for model in models: print("\n" + "=" * 80) print(f"TESTING MODEL CONFIG: {model.model_path}") print(f" TP Size: {model.tp_size}") print(f" Extra Args: {model.extra_args}") print("=" * 80) model_result = { "model": model.model_path, "variant": model.variant, "perf_result": None, "accuracy_result": None, "tool_call_result": None, "errors": [], } # Run performance test if run_perf: perf_result: PerformanceTestResult = run_performance_test( model=model, perf_runner=perf_runner, batch_sizes=performance_params.batch_sizes, input_lens=performance_params.input_lens, output_lens=performance_params.output_lens, is_vlm=is_vlm, dataset_name=performance_params.dataset_name, spec_accept_length_threshold=performance_params.spec_accept_length_threshold, ) model_result["perf_result"] = perf_result if not perf_result.passed: all_passed = False model_result["errors"].append(perf_result.error) # Wait for GPU memory and port cleanup print("\nWaiting 20 seconds for resource cleanup...") time.sleep(20) # Run accuracy test if run_accuracy: acc_result: AccuracyTestResult = run_accuracy_test( model=model, params=accuracy_params, base_url=base_url, ) model_result["accuracy_result"] = acc_result if not acc_result.passed: all_passed = False model_result["errors"].append(acc_result.error) # Wait for GPU memory and port cleanup print("\nWaiting 20 seconds for resource cleanup...") time.sleep(20) # Run tool call test if run_tool_call: tc_result: ToolCallTestResult = run_tool_call_test( model=model, params=tool_call_params, base_url=base_url, ) model_result["tool_call_result"] = tc_result if not tc_result.passed: all_passed = False model_result["errors"].extend(tc_result.failures) print("\nWaiting 20 seconds for resource cleanup...") time.sleep(20) all_results.append(model_result) # Write performance report if we ran perf tests if run_perf and perf_runner: perf_runner.write_final_report() # Write accuracy results to GitHub summary if in CI if run_accuracy and is_in_ci(): accuracy_results = [ r["accuracy_result"] for r in all_results if r["accuracy_result"] ] write_accuracy_github_summary( test_name, accuracy_params.dataset, accuracy_results ) # Print summary print("\n" + "=" * 60) print(f"{test_name} Results Summary") if run_accuracy: print(f"Dataset: {accuracy_params.dataset}") print(f"Baseline: {accuracy_params.baseline_accuracy}") print("=" * 60) for i, model_result in enumerate(all_results): print(f"\nModel {i + 1}: {model_result['model']}") if run_perf and model_result["perf_result"]: perf = model_result["perf_result"] throughput_str = ( f", output: {perf.output_throughput:.1f} tok/s" if perf.output_throughput else "" ) accept_str = ( f", accept_len: {perf.avg_spec_accept_length:.2f}" if perf.avg_spec_accept_length else "" ) print( f" Performance: {'PASS' if perf.passed else 'FAIL'}{throughput_str}{accept_str}" ) if run_accuracy and model_result["accuracy_result"]: acc = model_result["accuracy_result"] print(f" Accuracy: {'PASS' if acc.passed else 'FAIL'}") if acc.score is not None: print(f" Score: {acc.score:.3f}") if run_tool_call and model_result["tool_call_result"]: tc = model_result["tool_call_result"] print( f" Tool Call: {'PASS' if tc.passed else 'FAIL'} ({tc.num_passed}/{tc.num_total})" ) if model_result["errors"]: print(f" Errors: {model_result['errors']}") print("\n" + "=" * 60) print(f"OVERALL: {'ALL TESTS PASSED' if all_passed else 'SOME TESTS FAILED'}") print("=" * 60 + "\n") # Raise assertion error if any test failed if not all_passed: # Build detailed failure summary failure_lines = [] for i, r in enumerate(all_results): # Check for errors OR any failed test result (handles edge case where # a test fails but error is None/empty) has_failed_test = ( (r.get("perf_result") and not r["perf_result"].passed) or (r.get("accuracy_result") and not r["accuracy_result"].passed) or (r.get("tool_call_result") and not r["tool_call_result"].passed) ) if r["errors"] or has_failed_test: # Identify which test types failed failed_tests = [] if r.get("perf_result") and not r["perf_result"].passed: failed_tests.append("performance") if r.get("accuracy_result") and not r["accuracy_result"].passed: failed_tests.append("accuracy") if r.get("tool_call_result") and not r["tool_call_result"].passed: tc = r["tool_call_result"] failed_tests.append(f"tool_call ({tc.num_passed}/{tc.num_total})") failed_test_str = ", ".join(failed_tests) if failed_tests else "unknown" error_str = "; ".join(str(e) for e in r["errors"]) variant_str = f" [{r['variant']}]" if r.get("variant") else "" failure_lines.append( f" Model {i + 1} ({r['model']}{variant_str}): {failed_test_str} - {error_str}" ) failure_summary = "\n".join(failure_lines) raise AssertionError(f"Tests failed:\n{failure_summary}") return { "all_passed": all_passed, "results": all_results, }