sgl-project--sglang
94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
625 行
23 KiB
Python
625 行
23 KiB
Python
import copy
|
|
import unittest
|
|
|
|
from sglang.srt.managers.io_struct import GenerateReqInput
|
|
from sglang.test.ci.ci_register import (
|
|
register_amd_ci,
|
|
register_cpu_ci,
|
|
register_cuda_ci,
|
|
)
|
|
from sglang.test.test_utils import (
|
|
DEFAULT_SMALL_MODEL_NAME_FOR_TEST,
|
|
DEFAULT_URL_FOR_TEST,
|
|
CustomTestCase,
|
|
)
|
|
|
|
register_cuda_ci(est_time=8, stage="base-b", runner_config="1-gpu-large")
|
|
register_amd_ci(est_time=8, suite="stage-b-test-1-gpu-small-amd")
|
|
register_cpu_ci(est_time=8, suite="base-c-test-cpu")
|
|
|
|
|
|
class TestGenerateReqInputNormalization(CustomTestCase):
|
|
"""Test the normalization of GenerateReqInput for batch processing and different input formats."""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.model = DEFAULT_SMALL_MODEL_NAME_FOR_TEST
|
|
cls.base_url = DEFAULT_URL_FOR_TEST
|
|
|
|
def setUp(self):
|
|
# Common setup for all tests
|
|
self.base_req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
sampling_params=[{}, {}],
|
|
rid=["id1", "id2"],
|
|
)
|
|
|
|
def test_single_image_to_list_of_lists(self):
|
|
"""Test that a single image is converted to a list of single-image lists."""
|
|
req = copy.deepcopy(self.base_req)
|
|
req.image_data = "single_image.jpg" # A single image (non-list)
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Should be converted to [[image], [image]]
|
|
self.assertEqual(len(req.image_data), 2)
|
|
self.assertEqual(len(req.image_data[0]), 1)
|
|
self.assertEqual(len(req.image_data[1]), 1)
|
|
self.assertEqual(req.image_data[0][0], "single_image.jpg")
|
|
self.assertEqual(req.image_data[1][0], "single_image.jpg")
|
|
|
|
# Check modalities
|
|
self.assertEqual(req.modalities, ["image", "image"])
|
|
|
|
def test_list_of_images_to_list_of_lists(self):
|
|
"""Test that a list of images is converted to a list of single-image lists."""
|
|
req = copy.deepcopy(self.base_req)
|
|
req.image_data = ["image1.jpg", "image2.jpg"] # List of images
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Should be converted to [[image1], [image2]]
|
|
self.assertEqual(len(req.image_data), 2)
|
|
self.assertEqual(len(req.image_data[0]), 1)
|
|
self.assertEqual(len(req.image_data[1]), 1)
|
|
self.assertEqual(req.image_data[0][0], "image1.jpg")
|
|
self.assertEqual(req.image_data[1][0], "image2.jpg")
|
|
|
|
# Check modalities
|
|
self.assertEqual(req.modalities, ["image", "image"])
|
|
|
|
def test_list_of_lists_with_different_modalities(self):
|
|
"""Test handling of list of lists of images with different modalities."""
|
|
req = copy.deepcopy(self.base_req)
|
|
req.image_data = [
|
|
["image1.jpg"], # Single image (image modality)
|
|
["image2.jpg", "image3.jpg"], # Multiple images (multi-images modality)
|
|
]
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Structure should remain the same
|
|
self.assertEqual(len(req.image_data), 2)
|
|
self.assertEqual(len(req.image_data[0]), 1)
|
|
self.assertEqual(len(req.image_data[1]), 2)
|
|
|
|
# Check modalities
|
|
self.assertEqual(req.modalities, ["image", "multi-images"])
|
|
|
|
def test_list_of_lists_with_none_values(self):
|
|
"""Test handling of list of lists with None values."""
|
|
req = copy.deepcopy(self.base_req)
|
|
req.image_data = [
|
|
[None], # None value
|
|
["image.jpg"], # Single image
|
|
]
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Structure should remain the same
|
|
self.assertEqual(len(req.image_data), 2)
|
|
self.assertEqual(len(req.image_data[0]), 1)
|
|
self.assertEqual(len(req.image_data[1]), 1)
|
|
|
|
# Check modalities
|
|
self.assertEqual(req.modalities, [None, "image"])
|
|
|
|
def test_expanding_parallel_sample_correlation(self):
|
|
"""Test that when expanding with parallel samples, prompts, images and modalities are properly correlated."""
|
|
req = copy.deepcopy(self.base_req)
|
|
req.text = ["Prompt 1", "Prompt 2"]
|
|
req.image_data = [
|
|
["image1.jpg"],
|
|
["image2.jpg", "image3.jpg"],
|
|
]
|
|
req.sampling_params = {"n": 3} # All prompts get 3 samples
|
|
|
|
# Define expected values before normalization
|
|
expected_text = req.text * 3
|
|
expected_images = req.image_data * 3
|
|
expected_modalities = ["image", "multi-images"] * 3
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Should be expanded to 6 items (2 original * 3 parallel)
|
|
self.assertEqual(len(req.image_data), 6)
|
|
|
|
# Check that images are properly expanded
|
|
self.assertEqual(req.image_data, expected_images)
|
|
|
|
# Check modalities
|
|
self.assertEqual(req.modalities, expected_modalities)
|
|
|
|
# Ensure that text items are properly duplicated too
|
|
self.assertEqual(req.text, expected_text)
|
|
|
|
def test_specific_parallel_n_per_sample(self):
|
|
"""Test parallel expansion when different samples have different n values."""
|
|
req = copy.deepcopy(self.base_req)
|
|
req.text = ["Prompt 1", "Prompt 2"]
|
|
req.image_data = [
|
|
["image1.jpg"],
|
|
["image2.jpg", "image3.jpg"],
|
|
]
|
|
req.sampling_params = [
|
|
{"n": 2},
|
|
{"n": 2},
|
|
] # First prompt gets 2 samples, second prompt gets 2 samples
|
|
|
|
expected_images = req.image_data * 2
|
|
expected_modalities = ["image", "multi-images"] * 2
|
|
expected_text = req.text * 2
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Should be expanded to 4 items (2 original * 2 parallel)
|
|
self.assertEqual(len(req.image_data), 4)
|
|
|
|
# Check that the first 2 are copies for the first prompt
|
|
self.assertEqual(req.image_data, expected_images)
|
|
|
|
# Check modalities
|
|
self.assertEqual(req.modalities, expected_modalities)
|
|
|
|
# Check text expansion
|
|
self.assertEqual(req.text, expected_text)
|
|
|
|
def test_mixed_none_and_images_with_parallel_samples(self):
|
|
"""Test that when some batch items have images and others None, parallel expansion works correctly."""
|
|
req = copy.deepcopy(self.base_req)
|
|
req.text = ["Prompt 1", "Prompt 2", "Prompt 3"]
|
|
req.rid = ["id1", "id2", "id3"]
|
|
req.image_data = [
|
|
["image1.jpg"],
|
|
None,
|
|
["image3_1.jpg", "image3_2.jpg"],
|
|
]
|
|
req.sampling_params = {"n": 2} # All prompts get 2 samples
|
|
|
|
expected_images = req.image_data * 2
|
|
expected_modalities = ["image", None, "multi-images"] * 2
|
|
expected_text = req.text * 2
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Should be expanded to 6 items (3 original * 2 parallel)
|
|
self.assertEqual(len(req.image_data), 6)
|
|
|
|
# Check image data
|
|
self.assertEqual(req.image_data, expected_images)
|
|
|
|
# Check modalities
|
|
self.assertEqual(req.modalities, expected_modalities)
|
|
|
|
# Check text expansion
|
|
self.assertEqual(req.text, expected_text)
|
|
|
|
def test_correlation_with_sampling_params(self):
|
|
"""Test that sampling parameters are correctly correlated with prompts during expansion."""
|
|
req = copy.deepcopy(self.base_req)
|
|
req.text = ["Prompt 1", "Prompt 2"]
|
|
req.image_data = [
|
|
["image1.jpg"],
|
|
["image2.jpg"],
|
|
]
|
|
req.sampling_params = [
|
|
{"temperature": 0.7, "n": 2},
|
|
{"temperature": 0.9, "n": 2},
|
|
]
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Check sampling params expansion
|
|
self.assertEqual(len(req.sampling_params), 4)
|
|
self.assertEqual(req.sampling_params[0]["temperature"], 0.7)
|
|
self.assertEqual(req.sampling_params[1]["temperature"], 0.9)
|
|
self.assertEqual(req.sampling_params[2]["temperature"], 0.7)
|
|
self.assertEqual(req.sampling_params[3]["temperature"], 0.9)
|
|
|
|
# Should be expanded to 4 items (2 original * 2 parallel)
|
|
self.assertEqual(len(req.image_data), 4)
|
|
|
|
# Check correlation with images
|
|
self.assertEqual(req.image_data[0], ["image1.jpg"])
|
|
self.assertEqual(req.image_data[1], ["image2.jpg"])
|
|
self.assertEqual(req.image_data[2], ["image1.jpg"])
|
|
self.assertEqual(req.image_data[3], ["image2.jpg"])
|
|
|
|
def test_single_example_with_image(self):
|
|
"""Test handling of single example with image."""
|
|
req = GenerateReqInput(
|
|
text="Hello",
|
|
image_data="single_image.jpg",
|
|
)
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# For single examples, image_data doesn't get processed into lists
|
|
self.assertEqual(req.image_data, "single_image.jpg")
|
|
self.assertIsNone(req.modalities) # Modalities isn't set for single examples
|
|
|
|
def test_single_to_batch_with_parallel_sampling(self):
|
|
"""Test single example converted to batch with parallel sampling."""
|
|
req = GenerateReqInput(
|
|
text="Hello",
|
|
image_data="single_image.jpg",
|
|
sampling_params={"n": 3}, # parallel_sample_num = 3
|
|
)
|
|
|
|
# Define expected values before normalization
|
|
expected_text = ["Hello"] * 3
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Should be converted to batch with text=["Hello"]
|
|
self.assertEqual(req.text, expected_text)
|
|
|
|
# Image should be automatically wrapped to list of lists with length 1*3=3
|
|
self.assertEqual(len(req.image_data), 3)
|
|
self.assertEqual(req.image_data[0][0], "single_image.jpg")
|
|
self.assertEqual(req.image_data[1][0], "single_image.jpg")
|
|
self.assertEqual(req.image_data[2][0], "single_image.jpg")
|
|
|
|
# Modalities should be set for all 3 examples
|
|
self.assertEqual(req.modalities, ["image", "image", "image"])
|
|
|
|
def test_audio_data_handling(self):
|
|
"""Test handling of audio_data."""
|
|
req = copy.deepcopy(self.base_req)
|
|
req.audio_data = "audio.mp3" # Single audio
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Should be converted to ["audio.mp3", "audio.mp3"]
|
|
self.assertEqual(len(req.audio_data), 2)
|
|
self.assertEqual(req.audio_data[0], "audio.mp3")
|
|
self.assertEqual(req.audio_data[1], "audio.mp3")
|
|
|
|
# Test with list
|
|
req = copy.deepcopy(self.base_req)
|
|
req.audio_data = ["audio1.mp3", "audio2.mp3"]
|
|
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Should remain the same
|
|
self.assertEqual(len(req.audio_data), 2)
|
|
self.assertEqual(req.audio_data[0], "audio1.mp3")
|
|
self.assertEqual(req.audio_data[1], "audio2.mp3")
|
|
|
|
def test_input_ids_normalization(self):
|
|
"""Test normalization of input_ids instead of text."""
|
|
# Test single input_ids
|
|
req = GenerateReqInput(input_ids=[1, 2, 3])
|
|
req.normalize_batch_and_arguments()
|
|
self.assertTrue(req.is_single)
|
|
self.assertEqual(req.batch_size, 1)
|
|
|
|
# Test batch input_ids
|
|
req = GenerateReqInput(input_ids=[[1, 2, 3], [4, 5, 6]])
|
|
req.normalize_batch_and_arguments()
|
|
self.assertFalse(req.is_single)
|
|
self.assertEqual(req.batch_size, 2)
|
|
|
|
# Test with parallel sampling
|
|
req = GenerateReqInput(
|
|
input_ids=[[1, 2, 3], [4, 5, 6]], sampling_params={"n": 2}
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(len(req.input_ids), 4) # 2 original * 2 parallel
|
|
|
|
def test_input_embeds_normalization(self):
|
|
"""Test normalization of input_embeds."""
|
|
# Test single input_embeds
|
|
req = GenerateReqInput(input_embeds=[[0.1, 0.2], [0.3, 0.4]])
|
|
req.normalize_batch_and_arguments()
|
|
self.assertTrue(req.is_single)
|
|
self.assertEqual(req.batch_size, 1)
|
|
|
|
# Test batch input_embeds
|
|
req = GenerateReqInput(input_embeds=[[[0.1, 0.2]], [[0.3, 0.4]]])
|
|
req.normalize_batch_and_arguments()
|
|
self.assertFalse(req.is_single)
|
|
self.assertEqual(req.batch_size, 2)
|
|
|
|
def test_input_embeds_with_parallel_sampling(self):
|
|
"""Test input_embeds normalization with parallel sampling (n > 1)."""
|
|
# Test single input_embeds with parallel sampling
|
|
req = GenerateReqInput(
|
|
input_embeds=[[0.1, 0.2]], # single embedding vector
|
|
sampling_params={"n": 2},
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Should be converted from single to batch and then expanded
|
|
self.assertFalse(req.is_single)
|
|
self.assertEqual(len(req.input_embeds), 2)
|
|
# Both should be the same input_embeds
|
|
self.assertEqual(req.input_embeds[0], [[0.1, 0.2]])
|
|
self.assertEqual(req.input_embeds[1], [[0.1, 0.2]])
|
|
|
|
# Test batch input_embeds with parallel sampling
|
|
req = GenerateReqInput(
|
|
input_embeds=[[[0.1, 0.2]], [[0.3, 0.4]]], sampling_params={"n": 3}
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Should be expanded
|
|
self.assertFalse(req.is_single)
|
|
self.assertEqual(len(req.input_embeds), 6)
|
|
|
|
# Check that the expansion is correct
|
|
expected_embeds = [[[0.1, 0.2]], [[0.3, 0.4]]] * 3
|
|
self.assertEqual(req.input_embeds, expected_embeds)
|
|
|
|
# Test with different n values per sample (should raise error)
|
|
req = GenerateReqInput(
|
|
input_embeds=[[[0.1, 0.2]], [[0.3, 0.4]]],
|
|
sampling_params=[{"n": 2}, {"n": 3}],
|
|
)
|
|
with self.assertRaises(ValueError):
|
|
req.normalize_batch_and_arguments()
|
|
|
|
def test_lora_path_normalization(self):
|
|
"""Test normalization of lora_path."""
|
|
# Test single lora_path with batch input
|
|
req = GenerateReqInput(text=["Hello", "World"], lora_path="path/to/lora")
|
|
|
|
# Define expected lora_paths before normalization
|
|
expected_lora_paths = ["path/to/lora", "path/to/lora"]
|
|
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.lora_path, expected_lora_paths)
|
|
|
|
# Test list of lora_paths
|
|
req = GenerateReqInput(text=["Hello", "World"], lora_path=["path1", "path2"])
|
|
|
|
# Define expected lora_paths before normalization
|
|
expected_lora_paths = ["path1", "path2"]
|
|
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.lora_path, expected_lora_paths)
|
|
|
|
# Test with parallel sampling
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
lora_path=["path1", "path2"],
|
|
sampling_params={"n": 2},
|
|
)
|
|
|
|
# Define expected lora_paths before normalization
|
|
expected_lora_paths = ["path1", "path2"] * 2
|
|
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.lora_path, expected_lora_paths)
|
|
|
|
def test_extra_key_normalization(self):
|
|
"""Test normalization of extra_key."""
|
|
# Per-request list
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
extra_key=["tenant-A", "tenant-B"],
|
|
sampling_params=[{}, {}],
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.extra_key, ["tenant-A", "tenant-B"])
|
|
self.assertEqual(req[0].extra_key, "tenant-A")
|
|
self.assertEqual(req[1].extra_key, "tenant-B")
|
|
|
|
# Scalar broadcast
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
extra_key="shared",
|
|
sampling_params=[{}, {}],
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.extra_key, ["shared", "shared"])
|
|
|
|
# None stays None
|
|
req = GenerateReqInput(text=["Hello", "World"], sampling_params=[{}, {}])
|
|
req.normalize_batch_and_arguments()
|
|
self.assertIsNone(req.extra_key)
|
|
self.assertIsNone(req[0].extra_key)
|
|
|
|
# Parallel sampling expansion
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
extra_key=["tenant-A", "tenant-B"],
|
|
sampling_params={"n": 2},
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.extra_key, ["tenant-A", "tenant-B"] * 2)
|
|
|
|
# Wrong-length list
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
extra_key=["only-one"],
|
|
sampling_params=[{}, {}],
|
|
)
|
|
with self.assertRaisesRegex(ValueError, "batch size"):
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Non-batched scalar unchanged
|
|
req = GenerateReqInput(text="Hello", extra_key="solo")
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.extra_key, "solo")
|
|
|
|
def test_logprob_parameters_normalization(self):
|
|
"""Test normalization of logprob-related parameters."""
|
|
# Test single example
|
|
req = GenerateReqInput(
|
|
text="Hello",
|
|
return_logprob=True,
|
|
logprob_start_len=10,
|
|
top_logprobs_num=5,
|
|
token_ids_logprob=[7, 8, 9],
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.return_logprob, True)
|
|
self.assertEqual(req.logprob_start_len, 10)
|
|
self.assertEqual(req.top_logprobs_num, 5)
|
|
self.assertEqual(req.token_ids_logprob, [7, 8, 9])
|
|
|
|
# Test batch with scalar values
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
return_logprob=True,
|
|
logprob_start_len=10,
|
|
top_logprobs_num=5,
|
|
token_ids_logprob=[7, 8, 9],
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.return_logprob, [True, True])
|
|
self.assertEqual(req.logprob_start_len, [10, 10])
|
|
self.assertEqual(req.top_logprobs_num, [5, 5])
|
|
self.assertEqual(req.token_ids_logprob, [[7, 8, 9], [7, 8, 9]])
|
|
|
|
# Test batch with list values
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
return_logprob=[True, False],
|
|
logprob_start_len=[10, 5],
|
|
top_logprobs_num=[5, 3],
|
|
token_ids_logprob=[[7, 8, 9], [4, 5, 6]],
|
|
return_hidden_states=[False, False, True],
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.return_logprob, [True, False])
|
|
self.assertEqual(req.logprob_start_len, [10, 5])
|
|
self.assertEqual(req.top_logprobs_num, [5, 3])
|
|
self.assertEqual(req.token_ids_logprob, [[7, 8, 9], [4, 5, 6]])
|
|
self.assertEqual(req.return_hidden_states, [False, False, True])
|
|
|
|
def test_custom_logit_processor_normalization(self):
|
|
"""Test normalization of custom_logit_processor."""
|
|
# Test single processor
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"], custom_logit_processor="serialized_processor"
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(
|
|
req.custom_logit_processor, ["serialized_processor", "serialized_processor"]
|
|
)
|
|
|
|
# Test list of processors
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"], custom_logit_processor=["processor1", "processor2"]
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.custom_logit_processor, ["processor1", "processor2"])
|
|
|
|
def test_session_params_handling(self):
|
|
"""Test handling of session_params."""
|
|
# Test with dict
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"], session_params={"id": "session1", "offset": 10}
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.session_params, {"id": "session1", "offset": 10})
|
|
|
|
# Test with list of dicts
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
session_params=[{"id": "session1"}, {"id": "session2"}],
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.session_params, [{"id": "session1"}, {"id": "session2"}])
|
|
|
|
def test_session_id_handling(self):
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
session_id="session1",
|
|
sampling_params={"n": 2},
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
self.assertEqual(req.session_id, "session1")
|
|
self.assertIsNone(req.session_params)
|
|
self.assertEqual(req[2].session_id, "session1")
|
|
|
|
with self.assertRaisesRegex(ValueError, "cannot both be set"):
|
|
GenerateReqInput(
|
|
text="Hello",
|
|
session_id="explicit",
|
|
session_params={"id": "legacy"},
|
|
).normalize_batch_and_arguments()
|
|
|
|
def test_getitem_method(self):
|
|
"""Test the __getitem__ method."""
|
|
req = GenerateReqInput(
|
|
text=["Hello", "World"],
|
|
image_data=[["img1.jpg"], ["img2.jpg"]],
|
|
audio_data=["audio1.mp3", "audio2.mp3"],
|
|
sampling_params=[{"temp": 0.7}, {"temp": 0.8}],
|
|
rid=["id1", "id2"],
|
|
return_logprob=[True, False],
|
|
logprob_start_len=[10, 5],
|
|
top_logprobs_num=[5, 3],
|
|
token_ids_logprob=[[7, 8, 9], [4, 5, 6]],
|
|
stream=True,
|
|
log_metrics=True,
|
|
modalities=["image", "image"],
|
|
lora_path=["path1", "path2"],
|
|
custom_logit_processor=["processor1", "processor2"],
|
|
return_hidden_states=True,
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Get the first item
|
|
item0 = req[0]
|
|
self.assertEqual(item0.text, "Hello")
|
|
self.assertEqual(item0.image_data, ["img1.jpg"])
|
|
self.assertEqual(item0.audio_data, "audio1.mp3")
|
|
self.assertEqual(item0.sampling_params, {"temp": 0.7})
|
|
self.assertEqual(item0.rid, "id1")
|
|
self.assertEqual(item0.return_logprob, True)
|
|
self.assertEqual(item0.logprob_start_len, 10)
|
|
self.assertEqual(item0.top_logprobs_num, 5)
|
|
self.assertEqual(item0.token_ids_logprob, [7, 8, 9])
|
|
self.assertEqual(item0.stream, True)
|
|
self.assertEqual(item0.log_metrics, True)
|
|
self.assertEqual(item0.modalities, "image")
|
|
self.assertEqual(item0.lora_path, "path1")
|
|
self.assertEqual(item0.custom_logit_processor, "processor1")
|
|
self.assertEqual(item0.return_hidden_states, True)
|
|
|
|
def test_getitem_preserves_return_prompt_token_ids(self):
|
|
"""Batch subrequests must keep the prompt-token-id return flag."""
|
|
req = GenerateReqInput(
|
|
input_ids=[[1, 2, 3], [4, 5, 6]],
|
|
sampling_params=[{}, {}],
|
|
rid=["id1", "id2"],
|
|
return_prompt_token_ids=True,
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
|
|
self.assertTrue(req[0].return_prompt_token_ids)
|
|
self.assertTrue(req[1].return_prompt_token_ids)
|
|
|
|
def test_regenerate_rid(self):
|
|
"""Test the regenerate_rid method."""
|
|
req = GenerateReqInput(text="Hello")
|
|
req.normalize_batch_and_arguments()
|
|
|
|
original_rid = req.rid
|
|
new_rid = req.regenerate_rid()
|
|
|
|
self.assertNotEqual(original_rid, new_rid)
|
|
self.assertEqual(req.rid, new_rid)
|
|
|
|
def test_error_cases(self):
|
|
"""Test various error cases."""
|
|
# Test when neither text, input_ids, nor input_embeds is provided
|
|
with self.assertRaises(ValueError):
|
|
req = GenerateReqInput()
|
|
req.normalize_batch_and_arguments()
|
|
|
|
# Test when all of text, input_ids, and input_embeds are provided
|
|
with self.assertRaises(ValueError):
|
|
req = GenerateReqInput(
|
|
text="Hello", input_ids=[1, 2, 3], input_embeds=[[0.1, 0.2]]
|
|
)
|
|
req.normalize_batch_and_arguments()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|