[Utils] Make request dump robust to unpicklable server_args and large meta_info (#24767)

Co-authored-by: Byron Hsu <byron@periodiclabs.ai>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Byron Hsu
2026-05-09 21:41:41 -07:00
committed by GitHub
co-authored by Byron Hsu Cursor
parent 9578ba1b57
commit 1e6c6d1f07
3 changed files with 71 additions and 11 deletions
@@ -33,15 +33,30 @@ if __name__ == "__main__":
"--dump-requests-folder", type=str, default="/tmp/sglang_request_dump"
)
parser.add_argument("--dump-requests-threshold", type=int, default=1000)
parser.add_argument(
"--dump-requests-exclude-meta-keys",
type=str,
default=None,
help=(
"Comma-separated meta_info keys to strip from each dumped request "
"(e.g. 'routed_experts,hidden_states'). Pass an empty string to "
"keep all keys. If not set, the server default is used."
),
)
args = parser.parse_args()
response = requests.post(
args.url + "/configure_logging",
json={
"log_requests": args.log_requests,
"log_requests_level": args.log_requests_level, # Log full requests
"dump_requests_folder": args.dump_requests_folder,
"dump_requests_threshold": args.dump_requests_threshold,
},
)
payload = {
"log_requests": args.log_requests,
"log_requests_level": args.log_requests_level, # Log full requests
"dump_requests_folder": args.dump_requests_folder,
"dump_requests_threshold": args.dump_requests_threshold,
}
if args.dump_requests_exclude_meta_keys is not None:
payload["dump_requests_exclude_meta_keys"] = [
k.strip()
for k in args.dump_requests_exclude_meta_keys.split(",")
if k.strip()
]
response = requests.post(args.url + "/configure_logging", json=payload)
assert response.status_code == 200
+1
View File
@@ -1758,6 +1758,7 @@ class ConfigureLoggingReq(BaseReq):
dump_requests_folder: Optional[str] = None
dump_requests_threshold: Optional[int] = None
crash_dump_folder: Optional[str] = None
dump_requests_exclude_meta_keys: Optional[List[str]] = None
@dataclass
@@ -391,6 +391,10 @@ class TokenizerManager(TokenizerControlMixin, TokenizerManagerScoreMixin):
# Dumping
self.dump_requests_folder = "" # By default do not dump
self.dump_requests_threshold = 1000
self.dump_requests_exclude_meta_keys: List[str] = [
"routed_experts",
"hidden_states",
]
self.dump_request_list: List[Tuple] = []
self.crash_dump_request_list: deque[Tuple] = deque()
self.crash_dump_performed = False # Flag to ensure dump is only called once
@@ -1577,6 +1581,10 @@ class TokenizerManager(TokenizerControlMixin, TokenizerManagerScoreMixin):
self.dump_requests_folder = obj.dump_requests_folder
if obj.dump_requests_threshold is not None:
self.dump_requests_threshold = obj.dump_requests_threshold
if obj.dump_requests_exclude_meta_keys is not None:
self.dump_requests_exclude_meta_keys = list(
obj.dump_requests_exclude_meta_keys
)
if obj.crash_dump_folder is not None:
self.crash_dump_folder = obj.crash_dump_folder
logging.info(f"Config logging: {obj=}")
@@ -2193,6 +2201,16 @@ class TokenizerManager(TokenizerControlMixin, TokenizerManagerScoreMixin):
)
def dump_requests(self, state: ReqState, out_dict: dict):
if self.dump_requests_exclude_meta_keys and isinstance(
out_dict.get("meta_info"), dict
):
exclude = self.dump_requests_exclude_meta_keys
if any(k in out_dict["meta_info"] for k in exclude):
filtered_meta = {
k: v for k, v in out_dict["meta_info"].items() if k not in exclude
}
out_dict = {**out_dict, "meta_info": filtered_meta}
self.dump_request_list.append(
(
state.obj,
@@ -2243,7 +2261,20 @@ class TokenizerManager(TokenizerControlMixin, TokenizerManagerScoreMixin):
def background_task():
os.makedirs(os.path.dirname(filename), exist_ok=True)
with open(filename, "wb") as f:
pickle.dump(to_dump_with_server_args, f)
try:
pickle.dump(to_dump_with_server_args, f)
except Exception as e:
# When the server is launched with --trust-remote-code,
# server_args sometimes fails to pickle. Retry without
# server_args so the request data still gets persisted.
logger.error(
f"Failed to pickle dump with server_args: {e!r}; "
"retrying without server_args"
)
f.seek(0)
f.truncate()
to_dump_with_server_args["server_args"] = None
pickle.dump(to_dump_with_server_args, f)
asyncio.create_task(asyncio.to_thread(background_task))
@@ -2306,7 +2337,20 @@ class TokenizerManager(TokenizerControlMixin, TokenizerManagerScoreMixin):
"launch_command": " ".join(sys.argv),
}
with open(filename, "wb") as f:
pickle.dump(data_to_dump_with_server_args, f)
try:
pickle.dump(data_to_dump_with_server_args, f)
except Exception as e:
# When the server is launched with --trust-remote-code,
# server_args sometimes fails to pickle. Retry without
# server_args so the request data still gets persisted.
logger.error(
f"Failed to pickle dump with server_args: {e!r}; "
"retrying without server_args"
)
f.seek(0)
f.truncate()
data_to_dump_with_server_args["server_args"] = None
pickle.dump(data_to_dump_with_server_args, f)
logger.error(
f"Dumped {len(self.crash_dump_request_list)} finished and {len(unfinished_requests)} unfinished requests before crash to {filename}"
)