diff --git a/experimental/sgl-router/src/policies/kv_events/wire.rs b/experimental/sgl-router/src/policies/kv_events/wire.rs index 47714fa2a..be8bba6b6 100644 --- a/experimental/sgl-router/src/policies/kv_events/wire.rs +++ b/experimental/sgl-router/src/policies/kv_events/wire.rs @@ -5,7 +5,7 @@ //! `msgspec.msgpack`. Two struct families are involved: //! //! * `EventBatch` (the outer payload) — declared with -//! `array_like=True, omit_defaults=True, gc=False` (no tag). +//! `array_like=True, gc=False` (no tag). //! * `KVCacheEvent` (each inner event variant) — additionally declared //! with `tag=True`. //! @@ -17,9 +17,6 @@ //! of each inner event array, so an event is //! `[class_name_str, field1, field2, ...]`. The outer `EventBatch` //! array does **not** carry a tag prefix. -//! * `omit_defaults=True` allows trailing fields whose values equal their -//! declared defaults to be dropped from the array. The decoder therefore -//! accepts variable-length sequences for each struct shape. //! //! This module deserializes those bytes into Rust types and exposes a single //! [`decode_event_batch`] entry point. @@ -32,7 +29,7 @@ use serde::Deserialize; /// Top-level batch payload published by SGLang. /// /// Wire shape (`EventBatch`, `array_like`): -/// `[ts: f64, events: [...], attn_dp_rank: int_or_nil_or_omitted]`. +/// `[ts: f64, events: [...], attn_dp_rank: int_or_nil]`. /// SGLang declares `attn_dp_rank` as a Python `Optional[int]`; we decode /// it as `u32` since DP ranks are non-negative and bounded by the /// publisher's `dp_size`. @@ -43,7 +40,8 @@ pub struct KvEventBatch { /// Ordered list of cache events in this batch. pub events: Vec, /// Optional DP-attention rank that produced this batch. `None` if the - /// publisher emitted nil or omitted the field via `omit_defaults`. + /// publisher emitted nil. The decoder also accepts an omitted field for + /// compatibility. pub attn_dp_rank: Option, } @@ -332,8 +330,8 @@ impl<'de> Deserialize<'de> for BoundedU32Vec { // --------------------------------------------------------------------------- // Custom Deserialize impls — msgspec encodes these structs as msgpack arrays -// (not maps), and `omit_defaults=True` means trailing optional fields may be -// absent. We therefore implement `Deserialize` by hand against `SeqAccess`. +// (not maps). The visitors also accept absent trailing optional fields for +// compatibility. // --------------------------------------------------------------------------- impl<'de> Deserialize<'de> for KvEventBatch { @@ -731,8 +729,7 @@ mod tests { #[test] fn attn_dp_rank_omitted_decodes_as_none() { - // msgspec's `omit_defaults=True` may drop attn_dp_rank entirely from - // the wire array when it equals its default of None. + // Accept a payload that omits the optional trailing rank field. let event = build_all_blocks_cleared_bytes(); let bytes = build_batch_bytes(5.0, &[event], None, /* include_dp_field */ false); diff --git a/python/sglang/srt/disaggregation/kv_events.py b/python/sglang/srt/disaggregation/kv_events.py index 0a11009e0..fa0bdfc0a 100644 --- a/python/sglang/srt/disaggregation/kv_events.py +++ b/python/sglang/srt/disaggregation/kv_events.py @@ -61,7 +61,6 @@ def select_kv_publisher_dp_rank( class EventBatch( msgspec.Struct, array_like=True, # type: ignore[call-arg] - omit_defaults=True, # type: ignore[call-arg] gc=False, # type: ignore[call-arg] ): ts: float @@ -72,7 +71,6 @@ class EventBatch( class KVCacheEvent( msgspec.Struct, array_like=True, # type: ignore[call-arg] - omit_defaults=True, # type: ignore[call-arg] gc=False, # type: ignore[call-arg] tag=True, ): diff --git a/python/sglang/srt/sampling/sampling_params.py b/python/sglang/srt/sampling/sampling_params.py index b30af33bb..f3b3852e9 100644 --- a/python/sglang/srt/sampling/sampling_params.py +++ b/python/sglang/srt/sampling/sampling_params.py @@ -72,7 +72,7 @@ def raise_if_tokenizer_required( ) -class SamplingParams(msgspec.Struct, kw_only=True, omit_defaults=True): +class SamplingParams(msgspec.Struct, kw_only=True, array_like=True): """ The sampling parameters. @@ -106,10 +106,10 @@ class SamplingParams(msgspec.Struct, kw_only=True, omit_defaults=True): skip_special_tokens: bool = True spaces_between_special_tokens: bool = True no_stop_trim: bool = False - custom_params: Optional[Dict[str, CustomParamValue]] = None stream_interval: Optional[int] = None logit_bias: Optional[Dict[str, float]] = None sampling_seed: Optional[int] = None + custom_params: Optional[Dict[str, CustomParamValue]] = None # --- Internal fields (populated by __post_init__ or normalize(), not API-facing) --- stop_strs: Optional[Union[str, List[str]]] = None # from stop @@ -269,7 +269,7 @@ class SamplingParams(msgspec.Struct, kw_only=True, omit_defaults=True): tokenizer, self.stop_strs, self.stop_regex_strs, self.min_new_tokens ) - # Clear API input aliases so omit_defaults=True drops them from the wire. + # Clear API input aliases after normalizing them into internal fields. self.stop = None self.stop_regex = None self.is_normalized = True diff --git a/test/registered/unit/sampling/test_sampling_params.py b/test/registered/unit/sampling/test_sampling_params.py index f1985f6f4..518ca88b1 100644 --- a/test/registered/unit/sampling/test_sampling_params.py +++ b/test/registered/unit/sampling/test_sampling_params.py @@ -411,11 +411,6 @@ class TestSamplingParamsMsgspecStruct(CustomTestCase): self.assertTrue(sp.spaces_between_special_tokens) self.assertFalse(sp.no_stop_trim) - def test_msgpack_omits_default_fields(self): - encoded = msgspec.msgpack.encode(SamplingParams()) - - self.assertEqual(msgspec.msgpack.decode(encoded), {}) - def test_msgpack_round_trip_preserves_normalized_state(self): tokenizer = MagicMock() tokenizer.encode.side_effect = lambda s, add_special_tokens=False: {