diff --git a/python/sglang/srt/entrypoints/http_server.py b/python/sglang/srt/entrypoints/http_server.py index 076aa7c2d..9063bbe64 100644 --- a/python/sglang/srt/entrypoints/http_server.py +++ b/python/sglang/srt/entrypoints/http_server.py @@ -785,7 +785,7 @@ async def model_info(): config=resolving_view(_global_state.tokenizer_manager.server_args), model_config=model_config, ) - return result + return msgspec_to_builtins(result) @app.get("/get_weight_version") diff --git a/python/sglang/srt/utils/msgspec_utils.py b/python/sglang/srt/utils/msgspec_utils.py index f848c0028..8284af072 100644 --- a/python/sglang/srt/utils/msgspec_utils.py +++ b/python/sglang/srt/utils/msgspec_utils.py @@ -49,6 +49,9 @@ def msgspec_to_builtins(obj: Any) -> Any: for f in dataclasses.fields(obj) } + if isinstance(obj, type): + return f"{obj.__module__}.{obj.__qualname__}" + if isinstance(obj, dict): return {key: msgspec_to_builtins(value) for key, value in obj.items()} diff --git a/test/registered/unit/entrypoints/test_server_info.py b/test/registered/unit/entrypoints/test_server_info.py index 191c0bb22..60ee04b80 100644 --- a/test/registered/unit/entrypoints/test_server_info.py +++ b/test/registered/unit/entrypoints/test_server_info.py @@ -40,6 +40,10 @@ from sglang.test.test_utils import CustomTestCase register_cpu_ci(est_time=13, suite="base-a-test-cpu") +class _CustomModelLoader: + pass + + def _stub_tokenizer_manager( server_args: ServerArgs, get_internal_state=None ) -> TokenizerManager: @@ -57,6 +61,45 @@ def _stub_tokenizer_manager( return tokenizer_manager +class TestModelInfoSerialization(CustomTestCase): + def test_model_info_serializes_custom_loader_class(self): + server_args = ServerArgs(model_path="dummy") + values = { + "weight_version": None, + "load_format": _CustomModelLoader, + "reasoning_parser": None, + "tool_call_parser": None, + } + tokenizer_manager = SimpleNamespace( + model_config=SimpleNamespace( + is_image_understandable_model=False, + is_audio_understandable_model=False, + hf_config=SimpleNamespace( + model_type="test", architectures=["TestModel"] + ), + embedding_model_spec=None, + ), + model_path="dummy", + served_model_name="dummy", + server_args=server_args, + is_generation=True, + config_value=values.__getitem__, + ) + prior_state = http_server.get_global_state() + http_server.set_global_state( + SimpleNamespace(tokenizer_manager=tokenizer_manager) + ) + publish(server_args, role="tokenizer") + try: + payload = asyncio.run(http_server.model_info()) + finally: + http_server._global_state = prior_state + reset_context() + + self.assertEqual(payload["load_format"], f"{__name__}._CustomModelLoader") + json.dumps(payload) + + def _call_server_info_with( server_args: ServerArgs, internal_states: list[dict] | None = None,