Re-add the API serving timing metrics. (#14744)

Signed-off-by: zhanghaotong <zhanghaotong.zht@antgroup.com>
Co-authored-by: zhanghaotong <zhanghaotong.zht@antgroup.com>
This commit is contained in:
Liangsheng Yin
2025-12-10 10:17:48 +09:00
committed by GitHub
co-authored by zhanghaotong
parent a6dc7d2932
commit cbc7dcdaa7
3 changed files with 37 additions and 10 deletions
@@ -12,7 +12,7 @@ from fastapi import HTTPException, Request
from fastapi.responses import ORJSONResponse, StreamingResponse from fastapi.responses import ORJSONResponse, StreamingResponse
from sglang.srt.entrypoints.openai.protocol import ErrorResponse, OpenAIServingRequest from sglang.srt.entrypoints.openai.protocol import ErrorResponse, OpenAIServingRequest
from sglang.srt.managers.io_struct import GenerateReqInput from sglang.srt.managers.io_struct import EmbeddingReqInput, GenerateReqInput
from sglang.srt.server_args import ServerArgs from sglang.srt.server_args import ServerArgs
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -88,6 +88,9 @@ class OpenAIServingBase(ABC):
"""Handle the specific request type with common pattern """Handle the specific request type with common pattern
If you want to override this method, you should be careful to record the validation time. If you want to override this method, you should be careful to record the validation time.
""" """
received_time = time.time()
received_time_perf = time.perf_counter()
try: try:
# Validate request # Validate request
validation_start = time.perf_counter() validation_start = time.perf_counter()
@@ -100,8 +103,12 @@ class OpenAIServingBase(ABC):
adapted_request, processed_request = self._convert_to_internal_request( adapted_request, processed_request = self._convert_to_internal_request(
request, raw_request request, raw_request
) )
if hasattr(adapted_request, "validation_time"):
if isinstance(adapted_request, (GenerateReqInput, EmbeddingReqInput)):
# Only set timing fields if adapted_request supports them
adapted_request.validation_time = validation_time adapted_request.validation_time = validation_time
adapted_request.received_time = received_time
adapted_request.received_time_perf = received_time_perf
# Note(Xinyuan): raw_request below is only used for detecting the connection of the client # Note(Xinyuan): raw_request below is only used for detecting the connection of the client
if hasattr(request, "stream") and request.stream: if hasattr(request, "stream") and request.stream:
+27 -7
View File
@@ -110,6 +110,23 @@ class SpeculativeDecodingMetricsMixin:
spec_accepted_tokens: List[int] spec_accepted_tokens: List[int]
@dataclass
class APIServingTimingMixin:
# Validation step duration
validation_time: Optional[float] = None
# For metrics
received_time: Optional[float] = None
# Perf_counter equivalents for accurate time calculations
received_time_perf: Optional[float] = None
_API_SERVING_TIMING_MIXIN_FIELDS = tuple(
APIServingTimingMixin.__dataclass_fields__.keys()
)
# Parameters for a session # Parameters for a session
@dataclass @dataclass
class SessionParams: class SessionParams:
@@ -138,7 +155,7 @@ MultimodalDataInputFormat = Union[
@dataclass @dataclass
class GenerateReqInput(BaseReq): class GenerateReqInput(BaseReq, APIServingTimingMixin):
# The input prompt. It can be a single prompt or a batch of prompts. # The input prompt. It can be a single prompt or a batch of prompts.
text: Optional[Union[List[str], str]] = None text: Optional[Union[List[str], str]] = None
# The token ids for text; one can specify either text or input_ids # The token ids for text; one can specify either text or input_ids
@@ -201,9 +218,6 @@ class GenerateReqInput(BaseReq):
# For reasoning # For reasoning
reasoning: bool = False reasoning: bool = False
# Validation step duration
validation_time: Optional[float] = None
# For data parallel rank routing # For data parallel rank routing
data_parallel_rank: Optional[int] = None data_parallel_rank: Optional[int] = None
@@ -623,7 +637,6 @@ class GenerateReqInput(BaseReq):
decode_tp_size=( decode_tp_size=(
self.decode_tp_size[i] if self.decode_tp_size is not None else None self.decode_tp_size[i] if self.decode_tp_size is not None else None
), ),
validation_time=self.validation_time,
data_parallel_rank=( data_parallel_rank=(
self.data_parallel_rank if self.data_parallel_rank is not None else None self.data_parallel_rank if self.data_parallel_rank is not None else None
), ),
@@ -635,6 +648,10 @@ class GenerateReqInput(BaseReq):
return_bytes=self.return_bytes, return_bytes=self.return_bytes,
return_entropy=self.return_entropy, return_entropy=self.return_entropy,
http_worker_ipc=self.http_worker_ipc, http_worker_ipc=self.http_worker_ipc,
**{
field: getattr(self, field)
for field in _API_SERVING_TIMING_MIXIN_FIELDS
},
) )
@@ -724,7 +741,7 @@ class BatchTokenizedGenerateReqInput(BaseBatchReq):
@dataclass @dataclass
class EmbeddingReqInput(BaseReq): class EmbeddingReqInput(BaseReq, APIServingTimingMixin):
# The input prompt. It can be a single prompt or a batch of prompts. # The input prompt. It can be a single prompt or a batch of prompts.
text: Optional[Union[List[List[str]], List[str], str]] = None text: Optional[Union[List[List[str]], List[str], str]] = None
# The image input. It can be an image instance, file name, URL, or base64 encoded string. # The image input. It can be an image instance, file name, URL, or base64 encoded string.
@@ -840,9 +857,12 @@ class EmbeddingReqInput(BaseReq):
video_data=self.video_data[i] if self.video_data is not None else None, video_data=self.video_data[i] if self.video_data is not None else None,
sampling_params=self.sampling_params[i], sampling_params=self.sampling_params[i],
rid=self.rid[i], rid=self.rid[i],
validation_time=self.validation_time,
dimensions=self.dimensions, dimensions=self.dimensions,
http_worker_ipc=self.http_worker_ipc, http_worker_ipc=self.http_worker_ipc,
**{
field: getattr(self, field)
for field in _API_SERVING_TIMING_MIXIN_FIELDS
},
) )
@@ -408,7 +408,7 @@ class TokenizerManager(TokenizerCommunicatorMixin, TokenizerManagerMultiItemMixi
obj: Union[GenerateReqInput, EmbeddingReqInput], obj: Union[GenerateReqInput, EmbeddingReqInput],
request: Optional[fastapi.Request] = None, request: Optional[fastapi.Request] = None,
): ):
created_time = time.time() created_time = obj.received_time if obj.received_time else time.time()
self.auto_create_handle_loop() self.auto_create_handle_loop()
obj.normalize_batch_and_arguments() obj.normalize_batch_and_arguments()