@@ -66,6 +66,9 @@ def _build_mock_args():
6666 tokenizer_base_url = None ,
6767 dynamic_load_weight = False ,
6868 reasoning_parser = None ,
69+ task = None ,
70+ model_config_name = None ,
71+ tool_call_parser = None ,
6972 )
7073
7174
@@ -197,7 +200,7 @@ class WithDict:
197200 data = json .loads (resp .body .decode ("utf-8" ))
198201 # The object with __dict__ becomes its dict; the one without becomes null
199202 assert data .get ("with_dict" ) == {"a" : 1 }
200- assert "without_dict" in data and data ["without_dict" ] is None
203+ assert "without_dict" in data and isinstance ( data ["without_dict" ], str )
201204
202205
203206def test_metrics_app_routes_when_metrics_port_diff ():
@@ -264,5 +267,155 @@ class WithDict:
264267 assert resp2 .status_code == 200
265268 data = json .loads (resp2 .body .decode ("utf-8" ))
266269 assert data .get ("with_dict" ) == {"x" : 42 }
267- assert "without_dict" in data and data ["without_dict" ] is None
270+ assert "without_dict" in data and isinstance ( data ["without_dict" ], str )
268271 assert "env_config" in data
272+
273+
274+ def _reload_api_server ():
275+ """Helper: reload api_server with standard mocks, return the module."""
276+ with (
277+ patch ("fastdeploy.utils.FlexibleArgumentParser.parse_args" ) as mock_parse_args ,
278+ patch ("fastdeploy.utils.retrive_model_from_server" ) as mock_retrive_model ,
279+ patch ("fastdeploy.entrypoints.chat_utils.load_chat_template" ) as mock_load_template ,
280+ ):
281+ mock_parse_args .return_value = _build_mock_args ()
282+ mock_retrive_model .return_value = "test-model"
283+ mock_load_template .return_value = None
284+
285+ from fastdeploy .entrypoints .openai import api_server as api_server_mod
286+
287+ api_server = importlib .reload (api_server_mod )
288+ return api_server
289+
290+
291+ def test_config_info_server_config_matches_args ():
292+ """Verify server_config values are populated from args."""
293+ api_server = _reload_api_server ()
294+ from types import SimpleNamespace as NS
295+
296+ api_server .llm_engine = NS (cfg = NS ())
297+
298+ resp = _get_route (api_server .app , "/config-info" ).endpoint ()
299+ assert resp .status_code == 200
300+ data = json .loads (resp .body .decode ("utf-8" ))
301+
302+ sc = data ["server_config" ]
303+ assert sc ["host" ] == "0.0.0.0"
304+ assert sc ["port" ] == 8000
305+ assert sc ["workers" ] == 1
306+ assert sc ["metrics_port" ] is None
307+ assert sc ["controller_port" ] == - 1
308+ assert sc ["max_concurrency" ] == 16
309+ assert sc ["max_waiting_time" ] == - 1
310+ assert sc ["timeout" ] == 0
311+ assert sc ["timeout_graceful_shutdown" ] == 0
312+ assert sc ["served_model_name" ] is None
313+ assert sc ["task" ] is None
314+ assert sc ["model_config_name" ] is None
315+ assert sc ["tokenizer_base_url" ] is None
316+ assert sc ["enable_mm_output" ] is False
317+ assert sc ["tool_call_parser" ] is None
318+ assert sc ["tool_parser_plugin" ] is None
319+
320+
321+ def test_config_info_top_level_fields ():
322+ """Verify version_info, chat_template, device_info, env_config all present."""
323+ api_server = _reload_api_server ()
324+ from types import SimpleNamespace as NS
325+
326+ api_server .llm_engine = NS (cfg = NS (key = "val" ))
327+
328+ resp = _get_route (api_server .app , "/config-info" ).endpoint ()
329+ data = json .loads (resp .body .decode ("utf-8" ))
330+
331+ assert "version_info" in data
332+ assert "chat_template" in data
333+ assert "device_info" in data
334+ assert "env_config" in data
335+ assert isinstance (data ["env_config" ], dict )
336+ # cfg field should propagate
337+ assert data ["key" ] == "val"
338+
339+
340+ def test_config_info_process_object_set_and_frozenset ():
341+ """Cover process_object branch for set/frozenset -> list."""
342+ api_server = _reload_api_server ()
343+ from types import SimpleNamespace as NS
344+
345+ api_server .llm_engine = NS (
346+ cfg = NS (
347+ my_set = {3 , 1 , 2 },
348+ my_frozenset = frozenset (["b" , "a" ]),
349+ )
350+ )
351+
352+ resp = _get_route (api_server .app , "/config-info" ).endpoint ()
353+ assert resp .status_code == 200
354+ data = json .loads (resp .body .decode ("utf-8" ))
355+
356+ assert isinstance (data ["my_set" ], list )
357+ assert sorted (data ["my_set" ]) == [1 , 2 , 3 ]
358+ assert isinstance (data ["my_frozenset" ], list )
359+ assert sorted (data ["my_frozenset" ]) == ["a" , "b" ]
360+
361+
362+ def test_config_info_non_ascii_content ():
363+ """Cover ensure_ascii=False path with unicode in cfg."""
364+ api_server = _reload_api_server ()
365+ from types import SimpleNamespace as NS
366+
367+ api_server .llm_engine = NS (cfg = NS (desc = "中文描述" , emoji = "🚀" ))
368+
369+ resp = _get_route (api_server .app , "/config-info" ).endpoint ()
370+ assert resp .status_code == 200
371+ raw = resp .body .decode ("utf-8" )
372+ # Non-ASCII chars should appear directly, not as \uXXXX escapes
373+ assert "中文描述" in raw
374+ assert "🚀" in raw
375+ data = json .loads (raw )
376+ assert data ["desc" ] == "中文描述"
377+ assert data ["emoji" ] == "🚀"
378+
379+
380+ def test_config_info_cfg_fields_propagated ():
381+ """Verify that all cfg.__dict__ entries end up in the response."""
382+ api_server = _reload_api_server ()
383+ from types import SimpleNamespace as NS
384+
385+ api_server .llm_engine = NS (
386+ cfg = NS (
387+ model_name = "Qwen-7B" ,
388+ max_seq_len = 4096 ,
389+ use_fp16 = True ,
390+ parallel_config = None ,
391+ )
392+ )
393+
394+ resp = _get_route (api_server .app , "/config-info" ).endpoint ()
395+ data = json .loads (resp .body .decode ("utf-8" ))
396+
397+ assert data ["model_name" ] == "Qwen-7B"
398+ assert data ["max_seq_len" ] == 4096
399+ assert data ["use_fp16" ] is True
400+ assert data ["parallel_config" ] is None
401+
402+
403+ def test_config_info_nested_objects ():
404+ """Cover process_object with nested custom objects."""
405+ api_server = _reload_api_server ()
406+ from types import SimpleNamespace as NS
407+
408+ class Inner :
409+ pass
410+
411+ inner = Inner ()
412+ inner .lr = 0.01
413+ inner .steps = 100
414+
415+ api_server .llm_engine = NS (cfg = NS (train_config = inner ))
416+
417+ resp = _get_route (api_server .app , "/config-info" ).endpoint ()
418+ assert resp .status_code == 200
419+ data = json .loads (resp .body .decode ("utf-8" ))
420+
421+ assert data ["train_config" ] == {"lr" : 0.01 , "steps" : 100 }
0 commit comments