From 45c00daa1baec019c45e98e2b41f3e26965bddba Mon Sep 17 00:00:00 2001 From: Liangsheng Yin Date: Sun, 2 Aug 2026 23:55:06 -0700 Subject: [PATCH] [misc] Deep-merge nested config overrides and parse request bodies with orjson (#33351) --- python/sglang/srt/entrypoints/http_server.py | 28 +++++++++++++++++++ .../srt/utils/hf_transformers/config.py | 10 ++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/python/sglang/srt/entrypoints/http_server.py b/python/sglang/srt/entrypoints/http_server.py index 5962f1edc..74bd711ec 100644 --- a/python/sglang/srt/entrypoints/http_server.py +++ b/python/sglang/srt/entrypoints/http_server.py @@ -42,6 +42,7 @@ from typing import ( import aiohttp import numpy as np +import orjson import requests import uvicorn import uvloop @@ -60,6 +61,7 @@ from fastapi import ( from fastapi.exceptions import RequestValidationError from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import ORJSONResponse, Response, StreamingResponse +from fastapi.routing import APIRoute from sglang.srt.configs.embedding_model_spec import resolved_embedding_plan from sglang.srt.constants import HEALTH_CHECK_RID_PREFIX @@ -427,10 +429,33 @@ async def lifespan(fast_api_app: FastAPI): # Fast API +class ORJSONRequest(Request): + """Request whose ``json()`` uses orjson, for the tens-of-MB multimodal + bodies FastAPI would otherwise hand to stdlib json. Stricter than stdlib + on bare NaN/Infinity and >64-bit ints: those now 400 instead of parsing. + """ + + async def json(self) -> Any: + if not hasattr(self, "_json"): + self._json = orjson.loads(await self.body()) + return self._json + + +class ORJSONRoute(APIRoute): + def get_route_handler(self): + original_handler = super().get_route_handler() + + async def custom_handler(request: Request): + return await original_handler(ORJSONRequest(request.scope, request.receive)) + + return custom_handler + + app = FastAPI( lifespan=lifespan, openapi_url=None if get_bool_env_var("DISABLE_OPENAPI_DOC") else "/openapi.json", ) +app.router.route_class = ORJSONRoute app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -449,10 +474,13 @@ if envs.SGLANG_ENABLE_REQUEST_DECOMPRESSION.get(): # Include routers from sglang.srt.entrypoints.v1_loads import router as v1_loads_router +# route_class is per-router, so included routers need it set too. +v1_loads_router.route_class = ORJSONRoute app.include_router(v1_loads_router) from sglang.srt.entrypoints.elastic_ep import router as elastic_ep_router +elastic_ep_router.route_class = ORJSONRoute app.include_router(elastic_ep_router) diff --git a/python/sglang/srt/utils/hf_transformers/config.py b/python/sglang/srt/utils/hf_transformers/config.py index c5cb120b8..1c26fbeeb 100644 --- a/python/sglang/srt/utils/hf_transformers/config.py +++ b/python/sglang/srt/utils/hf_transformers/config.py @@ -254,7 +254,15 @@ def get_config( ) if model_override_args: - config.update(model_override_args) + # A plain update() setattrs a dict-valued override straight onto the + # config, so '{"text_config": {...}}' on a VLM would replace the whole + # sub-config with a dict and break attribute access downstream. + for key, value in model_override_args.items(): + current = getattr(config, key, None) + if isinstance(value, dict) and isinstance(current, PretrainedConfig): + current.update(value) + else: + setattr(config, key, value) if is_gguf: if config.model_type not in MODEL_FOR_CAUSAL_LM_MAPPING_NAMES: