Skip to content

Commit e8601e1

Browse files
CirilusKirill Shkolenkormccorm4
authored
fix(sglang): treat streamed usage metadata as optional (#11062)
Signed-off-by: Kirill Shkolenko <shkolenko.kirill@gmail.com> Co-authored-by: Kirill Shkolenko <shkolenko.kirill@gmail.com> Co-authored-by: Ryan McCormick <rmccormick@nvidia.com>
1 parent c42b874 commit e8601e1

2 files changed

Lines changed: 61 additions & 9 deletions

File tree

components/src/dynamo/sglang/request_handlers/llm/decode_handler.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -580,18 +580,23 @@ async def _process_token_stream(
580580
# as nvext.routed_experts); disaggregated_params stays KV-transfer only.
581581
out["engine_data"] = {"routed_experts": routed_experts}
582582
if finish_reason:
583-
input_tokens = meta_info["prompt_tokens"]
584-
completion_tokens = meta_info["completion_tokens"]
585-
cached_tokens = meta_info["cached_tokens"]
583+
input_tokens = meta_info.get("prompt_tokens")
584+
completion_tokens = meta_info.get("completion_tokens")
585+
cached_tokens = meta_info.get("cached_tokens")
586586
prefill_prompt_tokens_details = None
587587
if cached_tokens is not None and cached_tokens > 0:
588588
prefill_prompt_tokens_details = {"cached_tokens": cached_tokens}
589-
out["completion_usage"] = {
590-
"prompt_tokens": input_tokens,
591-
"completion_tokens": completion_tokens,
592-
"total_tokens": input_tokens + completion_tokens,
593-
"prompt_tokens_details": prefill_prompt_tokens_details,
594-
}
589+
if input_tokens is not None and completion_tokens is not None:
590+
completion_usage = {
591+
"prompt_tokens": input_tokens,
592+
"completion_tokens": completion_tokens,
593+
"total_tokens": input_tokens + completion_tokens,
594+
}
595+
if prefill_prompt_tokens_details is not None:
596+
completion_usage[
597+
"prompt_tokens_details"
598+
] = prefill_prompt_tokens_details
599+
out["completion_usage"] = completion_usage
595600
if metadata_uploader is not None:
596601
try:
597602
await metadata_uploader.upload_choice(output_idx, meta_info)

components/src/dynamo/sglang/tests/test_sglang_decode_handler.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,53 @@ async def test_metadata_upload_normalizes_numpy_values(tmp_path):
478478
assert uploaded_array["data"] == expected.tobytes()
479479

480480

481+
@pytest.mark.asyncio
482+
async def test_process_token_stream_treats_completion_usage_as_optional():
483+
handler = _new_decode_handler()
484+
485+
chunks = await _collect(
486+
handler._process_token_stream(
487+
_stream(
488+
[
489+
{
490+
"index": 0,
491+
"output_ids": [],
492+
"meta_info": {
493+
"id": "request-1",
494+
"finish_reason": {"type": "stop"},
495+
},
496+
},
497+
{
498+
"index": 1,
499+
"output_ids": [],
500+
"meta_info": {
501+
"id": "request-1",
502+
"finish_reason": {"type": "stop"},
503+
"prompt_tokens": 2,
504+
"completion_tokens": 3,
505+
},
506+
},
507+
]
508+
),
509+
_Context(),
510+
)
511+
)
512+
513+
assert chunks == [
514+
{"index": 0, "finish_reason": "stop", "token_ids": []},
515+
{
516+
"index": 1,
517+
"finish_reason": "stop",
518+
"token_ids": [],
519+
"completion_usage": {
520+
"prompt_tokens": 2,
521+
"completion_tokens": 3,
522+
"total_tokens": 5,
523+
},
524+
},
525+
]
526+
527+
481528
@pytest.mark.asyncio
482529
async def test_process_token_stream_tracks_logprobs_per_choice_index():
483530
handler = _new_decode_handler()

0 commit comments

Comments
 (0)