2025-02-02 14:58:18 -05:00
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2024-09-19 23:20:56 -07:00
|
|
|
import json
|
|
|
|
|
2024-10-08 08:31:26 -06:00
|
|
|
import pytest
|
|
|
|
|
|
|
|
from vllm.entrypoints.openai.cli_args import (make_arg_parser,
|
|
|
|
validate_parsed_serve_args)
|
2024-12-31 18:21:51 -08:00
|
|
|
from vllm.entrypoints.openai.serving_models import LoRAModulePath
|
2024-09-19 23:20:56 -07:00
|
|
|
from vllm.utils import FlexibleArgumentParser
|
|
|
|
|
2024-10-08 08:31:26 -06:00
|
|
|
from ...utils import VLLM_PATH
|
|
|
|
|
2024-09-19 23:20:56 -07:00
|
|
|
LORA_MODULE = {
|
|
|
|
"name": "module2",
|
|
|
|
"path": "/path/to/module2",
|
|
|
|
"base_model_name": "llama"
|
|
|
|
}
|
2024-10-08 08:31:26 -06:00
|
|
|
CHATML_JINJA_PATH = VLLM_PATH / "examples/template_chatml.jinja"
|
|
|
|
assert CHATML_JINJA_PATH.exists()
|
2024-09-19 23:20:56 -07:00
|
|
|
|
|
|
|
|
2024-10-08 08:31:26 -06:00
|
|
|
@pytest.fixture
|
|
|
|
def serve_parser():
|
|
|
|
parser = FlexibleArgumentParser(description="vLLM's remote OpenAI server.")
|
|
|
|
return make_arg_parser(parser)
|
2024-09-19 23:20:56 -07:00
|
|
|
|
|
|
|
|
2024-10-08 08:31:26 -06:00
|
|
|
### Tests for Lora module parsing
|
|
|
|
def test_valid_key_value_format(serve_parser):
|
|
|
|
# Test old format: name=path
|
|
|
|
args = serve_parser.parse_args([
|
|
|
|
'--lora-modules',
|
|
|
|
'module1=/path/to/module1',
|
|
|
|
])
|
|
|
|
expected = [LoRAModulePath(name='module1', path='/path/to/module1')]
|
|
|
|
assert args.lora_modules == expected
|
|
|
|
|
|
|
|
|
|
|
|
def test_valid_json_format(serve_parser):
|
|
|
|
# Test valid JSON format input
|
|
|
|
args = serve_parser.parse_args([
|
|
|
|
'--lora-modules',
|
|
|
|
json.dumps(LORA_MODULE),
|
|
|
|
])
|
|
|
|
expected = [
|
|
|
|
LoRAModulePath(name='module2',
|
|
|
|
path='/path/to/module2',
|
|
|
|
base_model_name='llama')
|
|
|
|
]
|
|
|
|
assert args.lora_modules == expected
|
|
|
|
|
|
|
|
|
|
|
|
def test_invalid_json_format(serve_parser):
|
|
|
|
# Test invalid JSON format input, missing closing brace
|
|
|
|
with pytest.raises(SystemExit):
|
|
|
|
serve_parser.parse_args([
|
|
|
|
'--lora-modules', '{"name": "module3", "path": "/path/to/module3"'
|
2024-09-19 23:20:56 -07:00
|
|
|
])
|
|
|
|
|
2024-10-08 08:31:26 -06:00
|
|
|
|
|
|
|
def test_invalid_type_error(serve_parser):
|
|
|
|
# Test type error when values are not JSON or key=value
|
|
|
|
with pytest.raises(SystemExit):
|
|
|
|
serve_parser.parse_args([
|
2024-09-19 23:20:56 -07:00
|
|
|
'--lora-modules',
|
2024-10-08 08:31:26 -06:00
|
|
|
'invalid_format' # This is not JSON or key=value format
|
2024-09-19 23:20:56 -07:00
|
|
|
])
|
2024-10-08 08:31:26 -06:00
|
|
|
|
|
|
|
|
|
|
|
def test_invalid_json_field(serve_parser):
|
|
|
|
# Test valid JSON format but missing required fields
|
|
|
|
with pytest.raises(SystemExit):
|
|
|
|
serve_parser.parse_args([
|
2024-09-19 23:20:56 -07:00
|
|
|
'--lora-modules',
|
2024-10-08 08:31:26 -06:00
|
|
|
'{"name": "module4"}' # Missing required 'path' field
|
2024-09-19 23:20:56 -07:00
|
|
|
])
|
|
|
|
|
|
|
|
|
2024-10-08 08:31:26 -06:00
|
|
|
def test_empty_values(serve_parser):
|
|
|
|
# Test when no LoRA modules are provided
|
|
|
|
args = serve_parser.parse_args(['--lora-modules', ''])
|
|
|
|
assert args.lora_modules == []
|
|
|
|
|
|
|
|
|
|
|
|
def test_multiple_valid_inputs(serve_parser):
|
|
|
|
# Test multiple valid inputs (both old and JSON format)
|
|
|
|
args = serve_parser.parse_args([
|
|
|
|
'--lora-modules',
|
|
|
|
'module1=/path/to/module1',
|
|
|
|
json.dumps(LORA_MODULE),
|
|
|
|
])
|
|
|
|
expected = [
|
|
|
|
LoRAModulePath(name='module1', path='/path/to/module1'),
|
|
|
|
LoRAModulePath(name='module2',
|
|
|
|
path='/path/to/module2',
|
|
|
|
base_model_name='llama')
|
|
|
|
]
|
|
|
|
assert args.lora_modules == expected
|
|
|
|
|
|
|
|
|
|
|
|
### Tests for serve argument validation that run prior to loading
|
|
|
|
def test_enable_auto_choice_passes_without_tool_call_parser(serve_parser):
|
|
|
|
"""Ensure validation fails if tool choice is enabled with no call parser"""
|
|
|
|
# If we enable-auto-tool-choice, explode with no tool-call-parser
|
|
|
|
args = serve_parser.parse_args(args=["--enable-auto-tool-choice"])
|
|
|
|
with pytest.raises(TypeError):
|
|
|
|
validate_parsed_serve_args(args)
|
|
|
|
|
|
|
|
|
|
|
|
def test_enable_auto_choice_passes_with_tool_call_parser(serve_parser):
|
|
|
|
"""Ensure validation passes with tool choice enabled with a call parser"""
|
|
|
|
args = serve_parser.parse_args(args=[
|
|
|
|
"--enable-auto-tool-choice",
|
|
|
|
"--tool-call-parser",
|
|
|
|
"mistral",
|
|
|
|
])
|
|
|
|
validate_parsed_serve_args(args)
|
|
|
|
|
|
|
|
|
2025-01-29 11:38:08 +08:00
|
|
|
def test_enable_auto_choice_fails_with_enable_reasoning(serve_parser):
|
|
|
|
"""Ensure validation fails if reasoning is enabled with auto tool choice"""
|
|
|
|
args = serve_parser.parse_args(args=[
|
|
|
|
"--enable-auto-tool-choice",
|
|
|
|
"--enable-reasoning",
|
|
|
|
])
|
|
|
|
with pytest.raises(TypeError):
|
|
|
|
validate_parsed_serve_args(args)
|
|
|
|
|
|
|
|
|
|
|
|
def test_enable_reasoning_passes_with_reasoning_parser(serve_parser):
|
|
|
|
"""Ensure validation passes if reasoning is enabled
|
|
|
|
with a reasoning parser"""
|
|
|
|
args = serve_parser.parse_args(args=[
|
|
|
|
"--enable-reasoning",
|
|
|
|
"--reasoning-parser",
|
|
|
|
"deepseek_r1",
|
|
|
|
])
|
|
|
|
validate_parsed_serve_args(args)
|
|
|
|
|
|
|
|
|
|
|
|
def test_enable_reasoning_fails_without_reasoning_parser(serve_parser):
|
|
|
|
"""Ensure validation fails if reasoning is enabled
|
|
|
|
without a reasoning parser"""
|
|
|
|
args = serve_parser.parse_args(args=["--enable-reasoning"])
|
|
|
|
with pytest.raises(TypeError):
|
|
|
|
validate_parsed_serve_args(args)
|
|
|
|
|
|
|
|
|
2024-10-08 08:31:26 -06:00
|
|
|
def test_chat_template_validation_for_happy_paths(serve_parser):
|
|
|
|
"""Ensure validation passes if the chat template exists"""
|
|
|
|
args = serve_parser.parse_args(
|
|
|
|
args=["--chat-template",
|
|
|
|
CHATML_JINJA_PATH.absolute().as_posix()])
|
|
|
|
validate_parsed_serve_args(args)
|
|
|
|
|
|
|
|
|
|
|
|
def test_chat_template_validation_for_sad_paths(serve_parser):
|
|
|
|
"""Ensure validation fails if the chat template doesn't exist"""
|
|
|
|
args = serve_parser.parse_args(args=["--chat-template", "does/not/exist"])
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
validate_parsed_serve_args(args)
|