From 890e9f1909d95bae2039d59a295f52689d02fa62 Mon Sep 17 00:00:00 2001 From: kesmeey <2561100473@qq.com> Date: Mon, 1 Dec 2025 15:37:54 +0800 Subject: [PATCH 1/6] test: improve ernie4_5_vl_processor.py test coverage --- tests/input/test_ernie4_5_vl_processor.py | 1311 +++++++++++++++++++++ 1 file changed, 1311 insertions(+) create mode 100644 tests/input/test_ernie4_5_vl_processor.py diff --git a/tests/input/test_ernie4_5_vl_processor.py b/tests/input/test_ernie4_5_vl_processor.py new file mode 100644 index 00000000000..596ef38eea1 --- /dev/null +++ b/tests/input/test_ernie4_5_vl_processor.py @@ -0,0 +1,1311 @@ +import unittest +from unittest.mock import MagicMock, patch + +import numpy as np + +from fastdeploy.input.ernie4_5_tokenizer import Ernie4_5Tokenizer +from fastdeploy.input.ernie4_5_vl_processor import Ernie4_5_VLProcessor +from fastdeploy.input.ernie4_5_vl_processor.image_preprocessor.image_preprocessor_adaptive import ( + AdaptiveImageProcessor, +) +from fastdeploy.input.ernie4_5_vl_processor.process import DataProcessor +from fastdeploy.input.utils import IDS_TYPE_FLAG + + +class TestErnie4_5_vl_ProcessorProcessResponseDictStreaming(unittest.TestCase): + def setUp(self): + with patch.object( + Ernie4_5_VLProcessor, "__init__", return_value=None + ) as mock_init: + self.processor = Ernie4_5_VLProcessor("model_path") + mock_init.side_effect = lambda *args, **kwargs: print( + f"__init__ called with {args}, {kwargs}" + ) + + self.processor.tokenizer = MagicMock() + self.processor.tokenizer.eos_token_id = 1 + self.processor.decode_status = {} + self.processor.reasoning_end_dict = {} + self.processor.tool_parser_dict = {} + self.processor.generation_config = MagicMock() + self.processor.eos_token_ids = [1] + self.processor.reasoning_parser = MagicMock() + self.processor._check_mm_limits = MagicMock() + self.processor.ernie4_5_processor = MagicMock() + self.processor.pack_outputs = MagicMock() + + def mock_ids2tokens(token_ids, task_id): + self.processor.decode_status[task_id] = "mock_decode_status" + return "delta_text", [2, 3], "previous_texts" + + self.processor.ids2tokens = mock_ids2tokens + + def mock_messages2ids(request, **kwargs): + if "chat_template" in kwargs: + return [1] + else: + return [0] + + def mock_apply_default_parameters(request): + return request + + self.processor._apply_default_parameters = mock_apply_default_parameters + + self.mock_reasoning_parser = MagicMock() + self.mock_reasoning_parser.__class__.__name__ = "ErnieX1ReasoningParser" + # self.mock_reasoning_parser.extract_reasoning_content_streaming.return_value = ("reasoning", "text") + self.processor.reasoning_parser = self.mock_reasoning_parser + + self.mock_tool_parser = MagicMock() + self.mock_tool_parser.extract_tool_calls_streaming.return_value = None + self.mock_tool_parser_obj = MagicMock() + self.mock_tool_parser_obj.return_value = self.mock_tool_parser + self.processor.tool_parser_obj = self.mock_tool_parser_obj + + def test_process_request_dict_with_options(self): + # Test with prompt_token_ids - enable_thinking defaults to True + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + # Test with prompt_token_ids and chat_template_kwargs + # Note: When prompt_token_ids is present, the code uses setdefault for enable_thinking + # and doesn't process chat_template_kwargs for enable_thinking + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": {"enable_thinking": True}, + "prompt_token_ids": [1, 1, 1], + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + # When prompt_token_ids is present, chat_template_kwargs enable_thinking is NOT processed + # The code uses setdefault which sets enable_thinking to True + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": {"enable_thinking": False}, + "prompt_token_ids": [1, 1, 1], + } + self.processor.process_request_dict(request_dict, 100) + # Since prompt_token_ids branch uses setdefault, enable_thinking defaults to True + self.assertEqual(request_dict["enable_thinking"], True) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, + "prompt_token_ids": [1, 1, 1], + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, + "prompt_token_ids": [1, 1, 1], + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, + "prompt_token_ids": [1, 1, 1], + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": {"options": {"thinking_mode": "123"}}, + "prompt_token_ids": [1, 1, 1], + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + def test_parse_processor_kwargs_valid(self): + """Test _parse_processor_kwargs with valid kwargs (lines 128-163)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor._parse_processor_kwargs = ( + Ernie4_5_VLProcessor._parse_processor_kwargs.__get__( + processor, Ernie4_5_VLProcessor + ) + ) + + valid_kwargs = { + "spatial_conv_size": 14, + "temporal_conv_size": 2, + "image_min_pixels": 1000, + "image_max_pixels": 10000, + } + result = processor._parse_processor_kwargs(valid_kwargs) + self.assertEqual(result, valid_kwargs) + + def test_parse_processor_kwargs_invalid_type(self): + """Test _parse_processor_kwargs with invalid type (line 155) + + Note: The implementation catches ValueError and returns empty dict with warning log, + rather than raising the exception. + """ + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + + invalid_kwargs = {"spatial_conv_size": "invalid"} # Should be int + # Implementation catches exception and returns empty dict + result = Ernie4_5_VLProcessor._parse_processor_kwargs( + processor, invalid_kwargs + ) + self.assertEqual(result, {}) + + def test_parse_processor_kwargs_not_dict(self): + """Test _parse_processor_kwargs with non-dict input (line 135) + + Note: The implementation catches ValueError and returns empty dict with warning log, + rather than raising the exception. + """ + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + + # Implementation catches exception and returns empty dict + result = Ernie4_5_VLProcessor._parse_processor_kwargs( + processor, "not a dict" + ) + self.assertEqual(result, {}) + + def test_parse_processor_kwargs_exception_handling(self): + """Test _parse_processor_kwargs exception handling (line 162)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor._parse_processor_kwargs = ( + Ernie4_5_VLProcessor._parse_processor_kwargs.__get__( + processor, Ernie4_5_VLProcessor + ) + ) + + # This should return empty dict on exception + with patch( + "fastdeploy.input.ernie4_5_vl_processor.ernie4_5_vl_processor.data_processor_logger" + ): + result = processor._parse_processor_kwargs(None) + self.assertEqual(result, {}) + + def test_parse_limits_valid(self): + """Test _parse_limits with valid input (lines 165-179)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor._parse_limits = Ernie4_5_VLProcessor._parse_limits.__get__( + processor, Ernie4_5_VLProcessor + ) + + valid_limits = {"image": 5, "video": 3} + result = processor._parse_limits(valid_limits) + self.assertEqual(result["image"], 5) + self.assertEqual(result["video"], 3) + self.assertEqual(result["audio"], 1) # Default value + + def test_parse_limits_invalid_type(self): + """Test _parse_limits with invalid type (line 174) + + Note: The implementation catches ValueError and returns DEFAULT_LIMITS with warning log, + rather than raising the exception. + """ + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + + # Implementation catches exception and returns default limits + result = Ernie4_5_VLProcessor._parse_limits(processor, "not a dict") + # Should return DEFAULT_LIMITS = {"image": 1, "video": 1, "audio": 1} + self.assertEqual(result["image"], 1) + self.assertEqual(result["video"], 1) + self.assertEqual(result["audio"], 1) + + def test_parse_limits_empty(self): + """Test _parse_limits with empty input (line 170)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor._parse_limits = Ernie4_5_VLProcessor._parse_limits.__get__( + processor, Ernie4_5_VLProcessor + ) + + result = processor._parse_limits(None) + self.assertEqual(result["image"], 1) + self.assertEqual(result["video"], 1) + self.assertEqual(result["audio"], 1) + + def test_check_mm_limits_with_dict(self): + """Test _check_mm_limits with dict input (lines 182-184)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.limit_mm_per_prompt = {"image": 2, "video": 1} + processor._check_mm_limits = Ernie4_5_VLProcessor._check_mm_limits.__get__( + processor, Ernie4_5_VLProcessor + ) + + mm_data = {"image": [1, 2], "video": [1]} + # Should not raise + processor._check_mm_limits(mm_data) + + def test_check_mm_limits_with_messages(self): + """Test _check_mm_limits with messages input (lines 186-195)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.limit_mm_per_prompt = {"image": 2, "video": 1} + processor._check_mm_limits = Ernie4_5_VLProcessor._check_mm_limits.__get__( + processor, Ernie4_5_VLProcessor + ) + + messages = [ + {"role": "user", "content": [{"type": "image", "data": "img1"}]}, + {"role": "user", "content": [{"type": "video", "data": "vid1"}]}, + ] + # Should not raise + processor._check_mm_limits(messages) + + def test_check_mm_limits_exceeded(self): + """Test _check_mm_limits when limit is exceeded (line 201)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.limit_mm_per_prompt = {"image": 1, "video": 1} + processor._check_mm_limits = Ernie4_5_VLProcessor._check_mm_limits.__get__( + processor, Ernie4_5_VLProcessor + ) + + mm_data = {"image": [1, 2, 3], "video": []} # 3 images, limit is 1 + with self.assertRaises(ValueError) as context: + processor._check_mm_limits(mm_data) + self.assertIn("Too many image items", str(context.exception)) + + def test_process_request(self): + """Test process_request method (lines 120-126)""" + from fastdeploy.engine.request import Request + + # Mock the process_request_dict method + self.processor.process_request_dict = MagicMock() + + # Create a mock Request object + mock_request = MagicMock(spec=Request) + mock_request.to_dict.return_value = { + "messages": [{"role": "user", "content": "Hello"}] + } + + # Mock Request.from_dict to return a mock request + with patch.object(Request, "from_dict") as mock_from_dict: + mock_result_request = MagicMock(spec=Request) + mock_from_dict.return_value = mock_result_request + + self.processor.process_request( + mock_request, max_model_len=100, chat_template_kwargs={"key": "value"} + ) + + # Verify to_dict was called + mock_request.to_dict.assert_called_once() + + # Verify process_request_dict was called + self.processor.process_request_dict.assert_called_once() + + # Verify from_dict was called + mock_from_dict.assert_called_once() + + def test_get_pad_id(self): + """Test get_pad_id method (line 86)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.tokenizer = MagicMock() + processor.tokenizer.pad_token_id = 100 + processor.get_pad_id = Ernie4_5_VLProcessor.get_pad_id.__get__( + processor, Ernie4_5_VLProcessor + ) + + result = processor.get_pad_id() + self.assertEqual(result, 100) + + def test_load_tokenizer(self): + """Test _load_tokenizer method (line 95)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + mock_tokenizer = MagicMock() + processor.ernie4_5_processor = MagicMock() + processor.ernie4_5_processor.tokenizer = mock_tokenizer + processor._load_tokenizer = Ernie4_5_VLProcessor._load_tokenizer.__get__( + processor, Ernie4_5_VLProcessor + ) + + processor._load_tokenizer() + self.assertEqual(processor.tokenizer, mock_tokenizer) + + def test_process_request_dict_with_stop_sequences(self): + """Test process_request_dict with stop sequences (lines 212-214)""" + self.processor.update_stop_seq = MagicMock(return_value=([100, 101], [1, 1])) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "stop": ["stop1", "stop2"], + } + self.processor.process_request_dict(request_dict, 100) + + self.processor.update_stop_seq.assert_called_once_with(["stop1", "stop2"]) + self.assertEqual(request_dict["stop_token_ids"], [100, 101]) + self.assertEqual(request_dict["stop_seqs_len"], [1, 1]) + + def test_process_request_dict_with_bad_words(self): + """Test process_request_dict with bad words (lines 219-220)""" + self.processor.update_bad_words = MagicMock(return_value=[[200], [201]]) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "bad_words": ["bad1", "bad2"], + } + self.processor.process_request_dict(request_dict, 100) + + self.processor.update_bad_words.assert_called_once() + self.assertEqual(request_dict["bad_words_token_ids"], [[200], [201]]) + + def test_process_request_dict_with_prompt(self): + """Test process_request_dict with prompt (lines 228-235)""" + self.processor.ernie4_5_processor.text2ids = MagicMock( + return_value={ + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + "cur_position": 3, + } + ) + + request_dict = { + "prompt": "Hello world", + "multimodal_data": {"image": [], "video": []}, + } + self.processor.process_request_dict(request_dict, 100) + + self.processor.ernie4_5_processor.text2ids.assert_called_once() + self.assertEqual(request_dict["prompt_tokens"], "Hello world") + + def test_process_request_dict_with_prompt_no_multimodal_data(self): + """Test process_request_dict with prompt and no multimodal_data (line 231)""" + self.processor.ernie4_5_processor.text2ids = MagicMock( + return_value={ + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + "cur_position": 3, + } + ) + + # Test without multimodal_data - should default to empty dict + request_dict = { + "prompt": "Hello world", + } + self.processor.process_request_dict(request_dict, 100) + + self.processor.ernie4_5_processor.text2ids.assert_called_once() + self.assertEqual(request_dict["prompt_tokens"], "Hello world") + + def test_process_request_dict_with_messages_only(self): + """Test process_request_dict with messages only (lines 236-259)""" + self.processor.ernie4_5_processor.request2ids = MagicMock( + return_value={ + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + "cur_position": 3, + } + ) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + } + self.processor.process_request_dict(request_dict, 100) + + self.processor.ernie4_5_processor.request2ids.assert_called_once() + self.assertEqual(request_dict["enable_thinking"], True) + + def test_process_request_dict_messages_with_chat_template_kwargs(self): + """Test process_request_dict with messages and chat_template_kwargs (lines 243-245)""" + self.processor.ernie4_5_processor.request2ids = MagicMock( + return_value={ + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + "cur_position": 3, + } + ) + + # Test that chat_template_kwargs values are copied to request + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": { + "custom_key": "custom_value", + "enable_thinking": False, + }, + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["custom_key"], "custom_value") + self.assertEqual(request_dict["enable_thinking"], False) + + def test_process_request_dict_messages_with_thinking_mode_close(self): + """Test process_request_dict with messages and thinking_mode close (lines 248-255)""" + self.processor.ernie4_5_processor.request2ids = MagicMock( + return_value={ + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + "cur_position": 3, + } + ) + + # Test thinking_mode = "close" + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], False) + + def test_process_request_dict_messages_with_thinking_mode_false(self): + """Test process_request_dict with messages and thinking_mode false (lines 248-255)""" + self.processor.ernie4_5_processor.request2ids = MagicMock( + return_value={ + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + "cur_position": 3, + } + ) + + # Test thinking_mode = "false" + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], False) + + def test_process_request_dict_messages_with_thinking_mode_open(self): + """Test process_request_dict with messages and thinking_mode open (lines 248-255)""" + self.processor.ernie4_5_processor.request2ids = MagicMock( + return_value={ + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + "cur_position": 3, + } + ) + + # Test thinking_mode = "open" (any value other than "close" or "false") + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + def test_process_request_dict_with_chat_template_kwargs_not_dict(self): + """Test process_request_dict with invalid chat_template_kwargs (line 247)""" + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": "not_a_dict", + } + + with self.assertRaises(ValueError) as context: + self.processor.process_request_dict(request_dict, 100) + self.assertIn("chat_template_kwargs must be a dict", str(context.exception)) + + def test_process_request_dict_no_valid_input(self): + """Test process_request_dict with no valid input (line 262)""" + request_dict = {} + + with self.assertRaises(ValueError) as context: + self.processor.process_request_dict(request_dict, 100) + self.assertIn("Request must contain", str(context.exception)) + + def test_process_request_dict_with_completion_token_ids(self): + """Test process_request_dict with completion_token_ids (line 264)""" + self.processor.append_completion_tokens = MagicMock() + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "completion_token_ids": [10, 11, 12], + } + self.processor.process_request_dict(request_dict, 100) + + self.processor.append_completion_tokens.assert_called_once() + + def test_process_request_dict_prompt_truncation(self): + """Test process_request_dict prompt truncation (line 275)""" + mock_outputs = MagicMock() + mock_outputs.__getitem__ = MagicMock( + side_effect=lambda k: { + "input_ids": np.array([1] * 150), + "token_type_ids": np.array([0] * 150), + "position_ids": np.array([[i, i, i] for i in range(150)]), + "images": None, + "grid_thw": None, + "image_type_ids": None, + "image_patch_id": 1001, + }.get(k) + ) + self.processor.pack_outputs = MagicMock(return_value=mock_outputs) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1] * 150, # 150 tokens + } + self.processor.process_request_dict(request_dict, 100) # max_model_len=100 + + # Should be truncated to max_model_len - 1 = 99 + self.assertEqual(len(request_dict["prompt_token_ids"]), 99) + + def test_process_request_dict_max_tokens_calculation(self): + """Test process_request_dict max_tokens calculation (lines 280, 286)""" + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "max_tokens": 200, # Larger than available + } + self.processor.process_request_dict(request_dict, 100) + + # max_tokens should be min(max_model_len - prompt_len, max_tokens) + self.assertLessEqual(request_dict["max_tokens"], 100 - 3) + + def test_process_request_dict_top_p_adjustment(self): + """Test process_request_dict top_p adjustment (line 288)""" + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "top_p": 1e-10, # Very small value + } + self.processor.process_request_dict(request_dict, 100) + + # top_p should be adjusted to _SAMPLING_EPS + self.assertGreaterEqual(request_dict["top_p"], 1e-5) + + def test_append_completion_tokens(self): + """Test append_completion_tokens method (lines 293-300)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.append_completion_tokens = ( + Ernie4_5_VLProcessor.append_completion_tokens.__get__( + processor, Ernie4_5_VLProcessor + ) + ) + + multimodal_inputs = { + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "cur_position": 3, + } + completion_token_ids = [10, 11, 12] + + processor.append_completion_tokens(multimodal_inputs, completion_token_ids) + + self.assertEqual(multimodal_inputs["input_ids"], [1, 2, 3, 10, 11, 12]) + self.assertEqual(multimodal_inputs["token_type_ids"], [0, 0, 0, 0, 0, 0]) + self.assertEqual(len(multimodal_inputs["position_ids"]), 6) + self.assertEqual(multimodal_inputs["cur_position"], 6) + + def test_pack_outputs_with_images(self): + """Test pack_outputs with images (lines 304-319)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.image_patch_id = 1001 + processor.pack_outputs = Ernie4_5_VLProcessor.pack_outputs.__get__( + processor, Ernie4_5_VLProcessor + ) + + outs = { + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [np.array([[1, 2], [3, 4]])], + "grid_thw": [np.array([[1, 2, 2]])], + "image_type_ids": [0], + } + + result = processor.pack_outputs(outs) + + self.assertIsNotNone(result["images"]) + self.assertIsNotNone(result["grid_thw"]) + self.assertIsNotNone(result["image_type_ids"]) + self.assertEqual(result["image_patch_id"], 1001) + self.assertIsInstance(result["input_ids"], np.ndarray) + self.assertIsInstance(result["token_type_ids"], np.ndarray) + self.assertIsInstance(result["position_ids"], np.ndarray) + + def test_pack_outputs_without_images(self): + """Test pack_outputs without images (lines 305-307)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.image_patch_id = 1001 + processor.pack_outputs = Ernie4_5_VLProcessor.pack_outputs.__get__( + processor, Ernie4_5_VLProcessor + ) + + outs = { + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + } + + result = processor.pack_outputs(outs) + + self.assertIsNone(result["images"]) + self.assertIsNone(result["grid_thw"]) + self.assertIsNone(result["image_type_ids"]) + + def test_process_response_dict_streaming(self): + """Test process_response_dict with stream=True (line 334)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.process_response_dict_streaming = MagicMock( + return_value={"text": "response"} + ) + processor.process_response_dict = ( + Ernie4_5_VLProcessor.process_response_dict.__get__( + processor, Ernie4_5_VLProcessor + ) + ) + + response_dict = {"ids": [1, 2, 3]} + result = processor.process_response_dict(response_dict, stream=True) + + processor.process_response_dict_streaming.assert_called_once() + self.assertEqual(result, {"text": "response"}) + + def test_process_response_dict_normal(self): + """Test process_response_dict with stream=False (line 336)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.process_response_dict_normal = MagicMock( + return_value={"text": "response"} + ) + processor.process_response_dict = ( + Ernie4_5_VLProcessor.process_response_dict.__get__( + processor, Ernie4_5_VLProcessor + ) + ) + + response_dict = {"ids": [1, 2, 3]} + result = processor.process_response_dict(response_dict, stream=False) + + processor.process_response_dict_normal.assert_called_once() + self.assertEqual(result, {"text": "response"}) + + def test_process_response_dict_enable_thinking_none(self): + """Test process_response_dict with enable_thinking=None (line 331)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.process_response_dict_streaming = MagicMock( + return_value={"text": "response"} + ) + processor.process_response_dict = ( + Ernie4_5_VLProcessor.process_response_dict.__get__( + processor, Ernie4_5_VLProcessor + ) + ) + + response_dict = {"ids": [1, 2, 3]} + processor.process_response_dict( + response_dict, stream=True, enable_thinking=None + ) + + # enable_thinking should default to True when None + processor.process_response_dict_streaming.assert_called_once_with( + response_dict, enable_thinking=True + ) + + def test_apply_default_parameters_with_dict(self): + """Test _apply_default_parameters with dict request (lines 102-116)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.generation_config = MagicMock() + processor.generation_config.top_p = 0.8 + processor.generation_config.temperature = 0.9 + processor._apply_default_parameters = ( + Ernie4_5_VLProcessor._apply_default_parameters.__get__( + processor, Ernie4_5_VLProcessor + ) + ) + + request = {} + result = processor._apply_default_parameters(request) + self.assertEqual(result["top_p"], 0.8) + self.assertEqual(result["temperature"], 0.9) + + def test_apply_default_parameters_with_object(self): + """Test _apply_default_parameters with object request (lines 108-109)""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.generation_config = MagicMock() + processor.generation_config.top_p = 0.8 + processor._apply_default_parameters = ( + Ernie4_5_VLProcessor._apply_default_parameters.__get__( + processor, Ernie4_5_VLProcessor + ) + ) + + class MockRequest: + def __init__(self): + self.top_p = None + self.temperature = None + + def get(self, key): + return getattr(self, key, None) + + def set(self, key, value): + setattr(self, key, value) + + request = MockRequest() + result = processor._apply_default_parameters(request) + self.assertEqual(result.top_p, 0.8) + + +class TestDataProcessorTargetMethods(unittest.TestCase): + def setUp(self): + self.mock_tokenizer = MagicMock(spec=Ernie4_5Tokenizer) + self.mock_tokenizer.ignored_index = -100 + self.mock_tokenizer.convert_tokens_to_ids.side_effect = ( + self._mock_convert_tokens_to_ids + ) + self.mock_tokenizer.chat_template = "mock_template" + self.mock_tokenizer.apply_chat_template.return_value = ( + "User: Hello<|image@placeholder|>" + ) + + def mock_load_tokenizer(dp_instance): + dp_instance.tokenizer = self.mock_tokenizer + + with patch.object( + DataProcessor, + "_load_tokenizer", + side_effect=mock_load_tokenizer, + autospec=True, + ): + with patch.object( + AdaptiveImageProcessor, "from_pretrained" + ) as mock_image_preprocessor: + mock_image_preprocessor.return_value = MagicMock() + self.data_processor = DataProcessor( + tokenizer_name="mock_tokenizer", + image_preprocessor_name="mock_image_preprocessor", + enable_processor_cache=False, + ) + self.data_processor.image_patch_id = 1001 + self.data_processor.image_start_id = 1002 + self.data_processor.image_end_id = 1003 + self.data_processor.video_start_id = 1004 + self.data_processor.video_end_id = 1005 + self.data_processor.role_prefixes = { + "user": "User: ", + "assistant": "Assistant: ", + } + self.data_processor.enable_processor_cache = False + self.data_processor.extract_mm_items = MagicMock( + return_value=([], [], [], [], None, [], []) + ) + + def _mock_convert_tokens_to_ids(self, token): + token_id_map = { + "<|begin_of_sentence|>": 101, + "<|end_of_sentence|>": 102, + "": 103, + "<|IMAGE_PLACEHOLDER|>": 1001, + "<|IMAGE_START|>": 1002, + "<|IMAGE_END|>": 1003, + "<|VIDEO_START|>": 1004, + "<|VIDEO_END|>": 1005, + } + return token_id_map.get(token, 999) + + def test_prompt_token_ids2outputs_only_prompt_token_ids(self): + test_prompt_token_ids = [101, 999, 998, 997, 102] + request = { + "prompt_token_ids": test_prompt_token_ids, + } + + outputs = self.data_processor.prompt_token_ids2outputs(request) + + prompt_len = len(test_prompt_token_ids) + + self.assertEqual( + outputs["input_ids"], + test_prompt_token_ids, + f"input_ids 涓嶅尮閰嶏細瀹為檯{outputs['input_ids']}锛岄鏈焄{test_prompt_token_ids}]", + ) + + self.assertEqual( + outputs["token_type_ids"], [IDS_TYPE_FLAG["text"]] * prompt_len + ) + + expected_position_ids = [[i] * 3 for i in range(prompt_len)] + self.assertEqual(outputs["position_ids"], expected_position_ids) + + self.assertEqual(outputs["cur_position"], prompt_len) + + self.assertEqual(len(outputs["images"]), 0) + self.assertEqual(len(outputs["grid_thw"]), 0) + self.assertEqual(len(outputs["mm_positions"]), 0) + self.assertEqual(len(outputs["mm_hashes"]), 0) + self.assertEqual(outputs["video_cnt"], 0) + self.assertEqual(outputs["num_input_image_tokens"], 0) + self.assertEqual(outputs["num_input_video_tokens"], 0) + + def test_prompt_token_ids2outputs_with_messages_no_mm(self): + test_prompt_token_ids = [101, 999, 998, 997, 102] + request = { + "prompt_token_ids": test_prompt_token_ids, + "messages": [{"role": "user", "content": "Hello World"}], + } + + self.data_processor.extract_mm_items.return_value = ( + [], + [], + [], + [], + None, + [], + [], + ) + + outputs = self.data_processor.prompt_token_ids2outputs(request) + + prompt_len = len(test_prompt_token_ids) + + self.assertEqual(outputs["input_ids"], test_prompt_token_ids) + + self.assertEqual( + outputs["token_type_ids"], [IDS_TYPE_FLAG["text"]] * prompt_len + ) + + expected_position_ids = [[i] * 3 for i in range(prompt_len)] + self.assertEqual(outputs["position_ids"], expected_position_ids) + + self.assertEqual(outputs["cur_position"], prompt_len) + + self.assertEqual(len(outputs["images"]), 0) + self.assertEqual(outputs["video_cnt"], 0) + self.assertEqual(outputs["num_input_image_tokens"], 0) + + def test_prompt_token_ids2outputs_add_image(self): + test_prompt_token_ids = [101, 1002, 1001, 1001, 1003, 102] + mock_img = MagicMock() + mock_img.height = 224 + mock_img.width = 224 + mock_img.convert.return_value = mock_img + request = { + "prompt_token_ids": test_prompt_token_ids, + "messages": [ + { + "role": "user", + "content": [ + {"type": "image_url", "image_url": mock_img, "uuid": "img_uuid"} + ], + } + ], + } + self.data_processor.extract_mm_items.return_value = ( + [mock_img], + [], + ["img_uuid"], + [], + None, + [], + [{"type": "image", "data": mock_img}], + ) + mock_resize = (None, (2, 4)) + self.data_processor.image_preprocessor.get_smarted_resize.return_value = ( + mock_resize + ) + mock_preprocess = { + "pixel_values": np.random.randn(1, 16, 16, 3), + "image_grid_thw": np.array([[2, 4]]), + } + self.data_processor.image_preprocessor.preprocess.return_value = mock_preprocess + # self.data_processor._compute_3d_positions = MagicMock(return_value=[[i]*3 for i in range(4)]) + outputs = self.data_processor.prompt_token_ids2outputs(request) + self.assertEqual(outputs["input_ids"], [101, 1002, 1001, 1001, 1003, 102]) + self.assertEqual( + outputs["token_type_ids"], + [ + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["image"], + IDS_TYPE_FLAG["image"], + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["text"], + ], + ) + self.assertEqual(len(outputs["position_ids"]), 6) + self.assertEqual(outputs["cur_position"], 6) + self.assertEqual(len(outputs["images"]), 1) + self.assertIsNotNone(outputs["images"][0]) + self.assertEqual(outputs["num_input_image_tokens"], 2) + self.assertEqual(len(outputs["mm_positions"]), 1) + self.assertEqual(len(outputs["mm_hashes"]), 1) + self.assertEqual(len(outputs["grid_thw"]), 1) + self.assertEqual(len(outputs["image_type_ids"]), 1) + + def test_prompt_token_ids2outputs_add_processed_image(self): + test_prompt_token_ids = [101, 1002, 1001, 1001, 1003, 102] + mock_img_data = np.random.randn(8, 28, 28) + mock_img_cache = (mock_img_data, {"thw": (1, 8, 8)}) + request = { + "prompt_token_ids": test_prompt_token_ids, + "messages": [ + { + "role": "user", + "content": [ + { + "type": "image_url", + "image_url": mock_img_cache, + "uuid": "img_uuid", + } + ], + } + ], + } + self.data_processor.extract_mm_items.return_value = ( + [mock_img_cache], + [], + ["img_uuid"], + [], + None, + [], + [{"type": "image", "data": mock_img_cache}], + ) + outputs = self.data_processor.prompt_token_ids2outputs(request) + self.assertEqual(outputs["input_ids"], [101, 1002, 1001, 1001, 1003, 102]) + self.assertEqual( + outputs["token_type_ids"], + [ + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["image"], + IDS_TYPE_FLAG["image"], + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["text"], + ], + ) + self.assertEqual(len(outputs["position_ids"]), 20) + self.assertEqual(outputs["cur_position"], 8) + self.assertEqual(len(outputs["images"]), 1) + self.assertIsNotNone(outputs["images"][0]) + self.assertEqual(len(outputs["mm_positions"]), 1) + self.assertEqual(outputs["mm_hashes"][0], "img_uuid") + self.assertEqual(len(outputs["grid_thw"]), 1) + self.assertEqual(len(outputs["image_type_ids"]), 1) + + def test_prompt_token_ids2outputs_add_video(self): + test_prompt_token_ids = [101, 1004, 1001, 1001, 1001, 1001, 1005, 102] + mock_frame1 = MagicMock() + mock_frame1.height = 224 + mock_frame1.width = 224 + mock_frame1.convert.return_value = mock_frame1 + mock_frame2 = MagicMock() + mock_frame2.height = 224 + mock_frame2.width = 224 + mock_frame2.convert.return_value = mock_frame2 + frames = [mock_frame1, mock_frame2] + request = { + "prompt_token_ids": test_prompt_token_ids, + "messages": [ + { + "role": "user", + "content": [ + {"type": "video_url", "video_url": frames, "uuid": "vid_uuid"} + ], + } + ], + } + self.data_processor.extract_mm_items.return_value = ( + [], + [frames], + [], + ["vid_uuid"], + None, + [], + [{"type": "video", "data": frames}], + ) + self.data_processor._load_and_process_video = MagicMock(return_value=frames) + patches_h, patches_w = 4, 4 + self.data_processor.image_preprocessor.get_smarted_resize.return_value = ( + None, + (patches_h, patches_w), + ) + mock_preprocess = { + "pixel_values_videos": np.random.randn(2, patches_h, patches_w, 3), + "video_grid_thw": np.array([[patches_h, patches_w]] * 2), + } + self.data_processor.image_preprocessor.preprocess.return_value = mock_preprocess + outputs = self.data_processor.prompt_token_ids2outputs(request) + self.assertEqual( + outputs["input_ids"], [101, 1004, 1001, 1001, 1001, 1001, 1005, 102] + ) + self.assertEqual( + outputs["token_type_ids"], + [ + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["video"], + IDS_TYPE_FLAG["video"], + IDS_TYPE_FLAG["video"], + IDS_TYPE_FLAG["video"], + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["text"], + ], + ) + self.assertEqual(len(outputs["position_ids"]), 8) + self.assertEqual(outputs["cur_position"], 6) + self.assertEqual(len(outputs["images"]), 1) + self.assertIsNotNone(outputs["images"][0]) + self.assertEqual(len(outputs["mm_positions"]), 1) + self.assertEqual(outputs["mm_hashes"][0], "vid_uuid") + self.assertEqual(len(outputs["grid_thw"]), 1) + self.assertEqual(len(outputs["image_type_ids"]), 2) + self.assertEqual(outputs["num_input_video_tokens"], 4) + + def test_prompt_token_ids2outputs_add_processed_video(self): + test_prompt_token_ids = [101, 1004, 1001, 1001, 1001, 1001, 1005, 102] + t, h, w = 2, 4, 4 + spatial_conv_size = self.data_processor.spatial_conv_size + temporal_conv_size = self.data_processor.temporal_conv_size + token_per_frame = (h // spatial_conv_size) * (w // spatial_conv_size) + num_tokens = (t // temporal_conv_size) * token_per_frame + mock_frames_data = np.random.randn( + num_tokens * spatial_conv_size**2 * temporal_conv_size, 28, 28 + ) + mock_frames_cache = (mock_frames_data, {"thw": (t, h, w)}) + request = { + "prompt_token_ids": test_prompt_token_ids, + "messages": [ + { + "role": "user", + "content": [ + {"type": "video", "data": mock_frames_cache, "uuid": "vid_uuid"} + ], + } + ], + } + self.data_processor.extract_mm_items.return_value = ( + [], + [mock_frames_cache], + [], + ["vid_uuid"], + None, + [], + [{"type": "video", "data": mock_frames_cache}], + ) + outputs = self.data_processor.prompt_token_ids2outputs(request) + self.assertEqual( + outputs["input_ids"], [101, 1004, 1001, 1001, 1001, 1001, 1005, 102] + ) + self.assertEqual( + outputs["token_type_ids"], + [ + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["video"], + IDS_TYPE_FLAG["video"], + IDS_TYPE_FLAG["video"], + IDS_TYPE_FLAG["video"], + IDS_TYPE_FLAG["text"], + IDS_TYPE_FLAG["text"], + ], + ) + self.assertEqual(len(outputs["position_ids"]), 8) + self.assertEqual(outputs["cur_position"], 6) + self.assertEqual(len(outputs["images"]), 1) + self.assertIsNotNone(outputs["images"][0]) + self.assertEqual(len(outputs["mm_positions"]), 1) + self.assertEqual(outputs["mm_hashes"][0], "vid_uuid") + self.assertEqual(len(outputs["grid_thw"]), 1) + self.assertEqual(len(outputs["image_type_ids"]), 2) + + def test_prompt_token_ids2outputs_add_image_token_len_mismatch(self): + test_prompt_token_ids = [101, 1002, 1001, 1001, 1001, 1003, 102] + mock_img = MagicMock() + mock_img.height = 224 + mock_img.width = 224 + mock_img.convert.return_value = mock_img + request = { + "prompt_token_ids": test_prompt_token_ids, + "messages": [ + { + "role": "user", + "content": [ + {"type": "image_url", "image_url": mock_img, "uuid": "img_uuid"} + ], + } + ], + } + self.data_processor.extract_mm_items.return_value = ( + [mock_img], + [], + ["img_uuid"], + [], + None, + [], + [{"type": "image", "data": mock_img}], + ) + patches_h, patches_w = 8, 8 + self.data_processor.image_preprocessor.get_smarted_resize.return_value = ( + None, + (patches_h, patches_w), + ) + mock_preprocess = { + "pixel_values": np.random.randn(1, patches_h, patches_w, 3), + "image_grid_thw": np.array([[patches_h, patches_w]]), + } + self.data_processor.image_preprocessor.preprocess.return_value = mock_preprocess + with self.assertRaises(ValueError) as ctx: + self.data_processor.prompt_token_ids2outputs(request) + self.assertIn("image tokens num not match the size", str(ctx.exception)) + + def test_prompt_token_ids2outputs_add_processed_image_token_len_mismatch(self): + test_prompt_token_ids = [101, 1002, 1001, 1001, 1003, 102] + spatial_conv_size = self.data_processor.spatial_conv_size + num_tokens = 4 + mock_img_data = np.random.randn(num_tokens * (spatial_conv_size**2), 28, 28) + mock_img_cache = (mock_img_data, {"thw": (1, 8, 8)}) + request = { + "prompt_token_ids": test_prompt_token_ids, + "messages": [ + { + "role": "user", + "content": [ + { + "type": "image_url", + "image_url": mock_img_cache, + "uuid": "img_uuid", + } + ], + } + ], + } + self.data_processor.extract_mm_items.return_value = ( + [mock_img_cache], + [], + ["img_uuid"], + [], + None, + [], + [{"type": "image", "data": mock_img_cache}], + ) + with self.assertRaises(ValueError) as ctx: + self.data_processor.prompt_token_ids2outputs(request) + self.assertIn("image tokens num not match the size", str(ctx.exception)) + + def test_prompt_token_ids2outputs_add_video_token_len_mismatch(self): + test_prompt_token_ids = [101, 1004, 1001, 1001, 1005, 102] + mock_frame1 = MagicMock() + mock_frame1.height = 224 + mock_frame1.width = 224 + mock_frame1.convert.return_value = mock_frame1 + mock_frame2 = MagicMock() + mock_frame2.height = 224 + mock_frame2.width = 224 + mock_frame2.convert.return_value = mock_frame2 + frames = [mock_frame1, mock_frame2] + request = { + "prompt_token_ids": test_prompt_token_ids, + "messages": [ + { + "role": "user", + "content": [ + {"type": "video_url", "video_url": frames, "uuid": "vid_uuid"} + ], + } + ], + } + self.data_processor.extract_mm_items.return_value = ( + [], + [frames], + [], + ["vid_uuid"], + None, + [], + [{"type": "video", "data": frames}], + ) + self.data_processor._load_and_process_video = MagicMock(return_value=frames) + patches_h, patches_w = 8, 8 + self.data_processor.image_preprocessor.get_smarted_resize.return_value = ( + None, + (patches_h, patches_w), + ) + mock_preprocess = { + "pixel_values_videos": np.random.randn(2, patches_h, patches_w, 3), + "video_grid_thw": np.array([[patches_h, patches_w]] * 2), + } + self.data_processor.image_preprocessor.preprocess.return_value = mock_preprocess + with self.assertRaises(ValueError) as ctx: + self.data_processor.prompt_token_ids2outputs(request) + self.assertIn("video tokens num not match the size", str(ctx.exception)) + + def test_prompt_token_ids2outputs_add_processed_video_token_len_mismatch(self): + test_prompt_token_ids = [101, 1004, 1001, 1005, 102] + t, h, w = 2, 8, 8 + spatial_conv_size = self.data_processor.spatial_conv_size + temporal_conv_size = self.data_processor.temporal_conv_size + + num_tokens = 4 + mock_frames_data = np.random.randn( + num_tokens * spatial_conv_size**2 * temporal_conv_size, 28, 28 + ) + mock_frames_cache = (mock_frames_data, {"thw": (t, h, w)}) + request = { + "prompt_token_ids": test_prompt_token_ids, + "messages": [ + { + "role": "user", + "content": [ + {"type": "video", "data": mock_frames_cache, "uuid": "vid_uuid"} + ], + } + ], + } + self.data_processor.extract_mm_items.return_value = ( + [], + [mock_frames_cache], + [], + ["vid_uuid"], + None, + [], + [{"type": "video", "data": mock_frames_cache}], + ) + with self.assertRaises(ValueError) as ctx: + self.data_processor.prompt_token_ids2outputs(request) + self.assertIn("video tokens num not match the size", str(ctx.exception)) + + +if __name__ == "__main__": + unittest.main() From 71ecf28a94e4d46332afef855a5abc1a4709bfe2 Mon Sep 17 00:00:00 2001 From: kesmeey <2561100473@qq.com> Date: Mon, 8 Dec 2025 16:22:57 +0800 Subject: [PATCH 2/6] update --- tests/input/test_ernie4_5_vl_processor.py | 387 +++++----------------- 1 file changed, 85 insertions(+), 302 deletions(-) diff --git a/tests/input/test_ernie4_5_vl_processor.py b/tests/input/test_ernie4_5_vl_processor.py index 596ef38eea1..4f480f071a0 100644 --- a/tests/input/test_ernie4_5_vl_processor.py +++ b/tests/input/test_ernie4_5_vl_processor.py @@ -14,13 +14,9 @@ class TestErnie4_5_vl_ProcessorProcessResponseDictStreaming(unittest.TestCase): def setUp(self): - with patch.object( - Ernie4_5_VLProcessor, "__init__", return_value=None - ) as mock_init: + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None) as mock_init: self.processor = Ernie4_5_VLProcessor("model_path") - mock_init.side_effect = lambda *args, **kwargs: print( - f"__init__ called with {args}, {kwargs}" - ) + mock_init.side_effect = lambda *args, **kwargs: print(f"__init__ called with {args}, {kwargs}") self.processor.tokenizer = MagicMock() self.processor.tokenizer.eos_token_id = 1 @@ -125,16 +121,15 @@ def test_process_request_dict_with_options(self): self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], True) - def test_parse_processor_kwargs_valid(self): - """Test _parse_processor_kwargs with valid kwargs (lines 128-163)""" + def test_parse_processor_kwargs(self): + """Test _parse_processor_kwargs with various inputs (lines 128-163)""" with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): processor = Ernie4_5_VLProcessor("model_path") - processor._parse_processor_kwargs = ( - Ernie4_5_VLProcessor._parse_processor_kwargs.__get__( - processor, Ernie4_5_VLProcessor - ) + processor._parse_processor_kwargs = Ernie4_5_VLProcessor._parse_processor_kwargs.__get__( + processor, Ernie4_5_VLProcessor ) + # Test with valid kwargs valid_kwargs = { "spatial_conv_size": 14, "temporal_conv_size": 2, @@ -144,135 +139,65 @@ def test_parse_processor_kwargs_valid(self): result = processor._parse_processor_kwargs(valid_kwargs) self.assertEqual(result, valid_kwargs) - def test_parse_processor_kwargs_invalid_type(self): - """Test _parse_processor_kwargs with invalid type (line 155) - - Note: The implementation catches ValueError and returns empty dict with warning log, - rather than raising the exception. - """ - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - + # Test with invalid type (implementation catches exception and returns empty dict) invalid_kwargs = {"spatial_conv_size": "invalid"} # Should be int - # Implementation catches exception and returns empty dict - result = Ernie4_5_VLProcessor._parse_processor_kwargs( - processor, invalid_kwargs - ) + result = Ernie4_5_VLProcessor._parse_processor_kwargs(processor, invalid_kwargs) self.assertEqual(result, {}) - def test_parse_processor_kwargs_not_dict(self): - """Test _parse_processor_kwargs with non-dict input (line 135) - - Note: The implementation catches ValueError and returns empty dict with warning log, - rather than raising the exception. - """ - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - - # Implementation catches exception and returns empty dict - result = Ernie4_5_VLProcessor._parse_processor_kwargs( - processor, "not a dict" - ) + # Test with non-dict input (implementation catches exception and returns empty dict) + result = Ernie4_5_VLProcessor._parse_processor_kwargs(processor, "not a dict") self.assertEqual(result, {}) - def test_parse_processor_kwargs_exception_handling(self): - """Test _parse_processor_kwargs exception handling (line 162)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor._parse_processor_kwargs = ( - Ernie4_5_VLProcessor._parse_processor_kwargs.__get__( - processor, Ernie4_5_VLProcessor - ) - ) - - # This should return empty dict on exception - with patch( - "fastdeploy.input.ernie4_5_vl_processor.ernie4_5_vl_processor.data_processor_logger" - ): + # Test exception handling with None + with patch("fastdeploy.input.ernie4_5_vl_processor.ernie4_5_vl_processor.data_processor_logger"): result = processor._parse_processor_kwargs(None) self.assertEqual(result, {}) - def test_parse_limits_valid(self): - """Test _parse_limits with valid input (lines 165-179)""" + def test_parse_limits(self): + """Test _parse_limits with various inputs (lines 165-179)""" with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): processor = Ernie4_5_VLProcessor("model_path") - processor._parse_limits = Ernie4_5_VLProcessor._parse_limits.__get__( - processor, Ernie4_5_VLProcessor - ) + processor._parse_limits = Ernie4_5_VLProcessor._parse_limits.__get__(processor, Ernie4_5_VLProcessor) + # Test with valid limits valid_limits = {"image": 5, "video": 3} result = processor._parse_limits(valid_limits) self.assertEqual(result["image"], 5) self.assertEqual(result["video"], 3) self.assertEqual(result["audio"], 1) # Default value - def test_parse_limits_invalid_type(self): - """Test _parse_limits with invalid type (line 174) - - Note: The implementation catches ValueError and returns DEFAULT_LIMITS with warning log, - rather than raising the exception. - """ - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - - # Implementation catches exception and returns default limits - result = Ernie4_5_VLProcessor._parse_limits(processor, "not a dict") - # Should return DEFAULT_LIMITS = {"image": 1, "video": 1, "audio": 1} + # Test with empty input (None) + result = processor._parse_limits(None) self.assertEqual(result["image"], 1) self.assertEqual(result["video"], 1) self.assertEqual(result["audio"], 1) - def test_parse_limits_empty(self): - """Test _parse_limits with empty input (line 170)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor._parse_limits = Ernie4_5_VLProcessor._parse_limits.__get__( - processor, Ernie4_5_VLProcessor - ) - - result = processor._parse_limits(None) + # Test with invalid type (implementation catches exception and returns default limits) + result = Ernie4_5_VLProcessor._parse_limits(processor, "not a dict") self.assertEqual(result["image"], 1) self.assertEqual(result["video"], 1) self.assertEqual(result["audio"], 1) - def test_check_mm_limits_with_dict(self): - """Test _check_mm_limits with dict input (lines 182-184)""" + def test_check_mm_limits(self): + """Test _check_mm_limits with various inputs (lines 182-201)""" with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): processor = Ernie4_5_VLProcessor("model_path") - processor.limit_mm_per_prompt = {"image": 2, "video": 1} - processor._check_mm_limits = Ernie4_5_VLProcessor._check_mm_limits.__get__( - processor, Ernie4_5_VLProcessor - ) + processor._check_mm_limits = Ernie4_5_VLProcessor._check_mm_limits.__get__(processor, Ernie4_5_VLProcessor) + # Test with dict input (should not raise) + processor.limit_mm_per_prompt = {"image": 2, "video": 1} mm_data = {"image": [1, 2], "video": [1]} - # Should not raise processor._check_mm_limits(mm_data) - def test_check_mm_limits_with_messages(self): - """Test _check_mm_limits with messages input (lines 186-195)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor.limit_mm_per_prompt = {"image": 2, "video": 1} - processor._check_mm_limits = Ernie4_5_VLProcessor._check_mm_limits.__get__( - processor, Ernie4_5_VLProcessor - ) - + # Test with messages input (should not raise) messages = [ {"role": "user", "content": [{"type": "image", "data": "img1"}]}, {"role": "user", "content": [{"type": "video", "data": "vid1"}]}, ] - # Should not raise processor._check_mm_limits(messages) - def test_check_mm_limits_exceeded(self): - """Test _check_mm_limits when limit is exceeded (line 201)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") + # Test when limit is exceeded (should raise ValueError) processor.limit_mm_per_prompt = {"image": 1, "video": 1} - processor._check_mm_limits = Ernie4_5_VLProcessor._check_mm_limits.__get__( - processor, Ernie4_5_VLProcessor - ) - mm_data = {"image": [1, 2, 3], "video": []} # 3 images, limit is 1 with self.assertRaises(ValueError) as context: processor._check_mm_limits(mm_data) @@ -287,18 +212,14 @@ def test_process_request(self): # Create a mock Request object mock_request = MagicMock(spec=Request) - mock_request.to_dict.return_value = { - "messages": [{"role": "user", "content": "Hello"}] - } + mock_request.to_dict.return_value = {"messages": [{"role": "user", "content": "Hello"}]} # Mock Request.from_dict to return a mock request with patch.object(Request, "from_dict") as mock_from_dict: mock_result_request = MagicMock(spec=Request) mock_from_dict.return_value = mock_result_request - self.processor.process_request( - mock_request, max_model_len=100, chat_template_kwargs={"key": "value"} - ) + self.processor.process_request(mock_request, max_model_len=100, chat_template_kwargs={"key": "value"}) # Verify to_dict was called mock_request.to_dict.assert_called_once() @@ -315,9 +236,7 @@ def test_get_pad_id(self): processor = Ernie4_5_VLProcessor("model_path") processor.tokenizer = MagicMock() processor.tokenizer.pad_token_id = 100 - processor.get_pad_id = Ernie4_5_VLProcessor.get_pad_id.__get__( - processor, Ernie4_5_VLProcessor - ) + processor.get_pad_id = Ernie4_5_VLProcessor.get_pad_id.__get__(processor, Ernie4_5_VLProcessor) result = processor.get_pad_id() self.assertEqual(result, 100) @@ -329,9 +248,7 @@ def test_load_tokenizer(self): mock_tokenizer = MagicMock() processor.ernie4_5_processor = MagicMock() processor.ernie4_5_processor.tokenizer = mock_tokenizer - processor._load_tokenizer = Ernie4_5_VLProcessor._load_tokenizer.__get__( - processor, Ernie4_5_VLProcessor - ) + processor._load_tokenizer = Ernie4_5_VLProcessor._load_tokenizer.__get__(processor, Ernie4_5_VLProcessor) processor._load_tokenizer() self.assertEqual(processor.tokenizer, mock_tokenizer) @@ -379,35 +296,21 @@ def test_process_request_dict_with_prompt(self): } ) + # Test with multimodal_data request_dict = { "prompt": "Hello world", "multimodal_data": {"image": [], "video": []}, } self.processor.process_request_dict(request_dict, 100) - self.processor.ernie4_5_processor.text2ids.assert_called_once() self.assertEqual(request_dict["prompt_tokens"], "Hello world") - def test_process_request_dict_with_prompt_no_multimodal_data(self): - """Test process_request_dict with prompt and no multimodal_data (line 231)""" - self.processor.ernie4_5_processor.text2ids = MagicMock( - return_value={ - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [], - "grid_thw": [], - "image_type_ids": [], - "cur_position": 3, - } - ) - # Test without multimodal_data - should default to empty dict + self.processor.ernie4_5_processor.text2ids.reset_mock() request_dict = { "prompt": "Hello world", } self.processor.process_request_dict(request_dict, 100) - self.processor.ernie4_5_processor.text2ids.assert_called_once() self.assertEqual(request_dict["prompt_tokens"], "Hello world") @@ -459,8 +362,8 @@ def test_process_request_dict_messages_with_chat_template_kwargs(self): self.assertEqual(request_dict["custom_key"], "custom_value") self.assertEqual(request_dict["enable_thinking"], False) - def test_process_request_dict_messages_with_thinking_mode_close(self): - """Test process_request_dict with messages and thinking_mode close (lines 248-255)""" + def test_process_request_dict_messages_with_thinking_mode(self): + """Test process_request_dict with messages and different thinking_mode values (lines 248-255)""" self.processor.ernie4_5_processor.request2ids = MagicMock( return_value={ "input_ids": [1, 2, 3], @@ -473,7 +376,7 @@ def test_process_request_dict_messages_with_thinking_mode_close(self): } ) - # Test thinking_mode = "close" + # Test thinking_mode = "close" (should set enable_thinking to False) request_dict = { "messages": [{"role": "user", "content": "Hello"}], "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, @@ -481,21 +384,7 @@ def test_process_request_dict_messages_with_thinking_mode_close(self): self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], False) - def test_process_request_dict_messages_with_thinking_mode_false(self): - """Test process_request_dict with messages and thinking_mode false (lines 248-255)""" - self.processor.ernie4_5_processor.request2ids = MagicMock( - return_value={ - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [], - "grid_thw": [], - "image_type_ids": [], - "cur_position": 3, - } - ) - - # Test thinking_mode = "false" + # Test thinking_mode = "false" (should set enable_thinking to False) request_dict = { "messages": [{"role": "user", "content": "Hello"}], "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, @@ -503,21 +392,7 @@ def test_process_request_dict_messages_with_thinking_mode_false(self): self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], False) - def test_process_request_dict_messages_with_thinking_mode_open(self): - """Test process_request_dict with messages and thinking_mode open (lines 248-255)""" - self.processor.ernie4_5_processor.request2ids = MagicMock( - return_value={ - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [], - "grid_thw": [], - "image_type_ids": [], - "cur_position": 3, - } - ) - - # Test thinking_mode = "open" (any value other than "close" or "false") + # Test thinking_mode = "open" (should set enable_thinking to True) request_dict = { "messages": [{"role": "user", "content": "Hello"}], "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, @@ -610,10 +485,8 @@ def test_append_completion_tokens(self): """Test append_completion_tokens method (lines 293-300)""" with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): processor = Ernie4_5_VLProcessor("model_path") - processor.append_completion_tokens = ( - Ernie4_5_VLProcessor.append_completion_tokens.__get__( - processor, Ernie4_5_VLProcessor - ) + processor.append_completion_tokens = Ernie4_5_VLProcessor.append_completion_tokens.__get__( + processor, Ernie4_5_VLProcessor ) multimodal_inputs = { @@ -631,16 +504,15 @@ def test_append_completion_tokens(self): self.assertEqual(len(multimodal_inputs["position_ids"]), 6) self.assertEqual(multimodal_inputs["cur_position"], 6) - def test_pack_outputs_with_images(self): - """Test pack_outputs with images (lines 304-319)""" + def test_pack_outputs(self): + """Test pack_outputs with and without images (lines 304-319)""" with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): processor = Ernie4_5_VLProcessor("model_path") processor.image_patch_id = 1001 - processor.pack_outputs = Ernie4_5_VLProcessor.pack_outputs.__get__( - processor, Ernie4_5_VLProcessor - ) + processor.pack_outputs = Ernie4_5_VLProcessor.pack_outputs.__get__(processor, Ernie4_5_VLProcessor) - outs = { + # Test with images + outs_with_images = { "input_ids": [1, 2, 3], "token_type_ids": [0, 0, 0], "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], @@ -649,8 +521,7 @@ def test_pack_outputs_with_images(self): "image_type_ids": [0], } - result = processor.pack_outputs(outs) - + result = processor.pack_outputs(outs_with_images) self.assertIsNotNone(result["images"]) self.assertIsNotNone(result["grid_thw"]) self.assertIsNotNone(result["image_type_ids"]) @@ -659,16 +530,8 @@ def test_pack_outputs_with_images(self): self.assertIsInstance(result["token_type_ids"], np.ndarray) self.assertIsInstance(result["position_ids"], np.ndarray) - def test_pack_outputs_without_images(self): - """Test pack_outputs without images (lines 305-307)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor.image_patch_id = 1001 - processor.pack_outputs = Ernie4_5_VLProcessor.pack_outputs.__get__( - processor, Ernie4_5_VLProcessor - ) - - outs = { + # Test without images + outs_without_images = { "input_ids": [1, 2, 3], "token_type_ids": [0, 0, 0], "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], @@ -677,103 +540,57 @@ def test_pack_outputs_without_images(self): "image_type_ids": [], } - result = processor.pack_outputs(outs) - + result = processor.pack_outputs(outs_without_images) self.assertIsNone(result["images"]) self.assertIsNone(result["grid_thw"]) self.assertIsNone(result["image_type_ids"]) - def test_process_response_dict_streaming(self): - """Test process_response_dict with stream=True (line 334)""" + def test_process_response_dict(self): + """Test process_response_dict with different parameters (lines 331-336)""" with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): processor = Ernie4_5_VLProcessor("model_path") - processor.process_response_dict_streaming = MagicMock( - return_value={"text": "response"} - ) - processor.process_response_dict = ( - Ernie4_5_VLProcessor.process_response_dict.__get__( - processor, Ernie4_5_VLProcessor - ) + processor.process_response_dict = Ernie4_5_VLProcessor.process_response_dict.__get__( + processor, Ernie4_5_VLProcessor ) + # Test with stream=True + processor.process_response_dict_streaming = MagicMock(return_value={"text": "response"}) response_dict = {"ids": [1, 2, 3]} result = processor.process_response_dict(response_dict, stream=True) - processor.process_response_dict_streaming.assert_called_once() self.assertEqual(result, {"text": "response"}) - def test_process_response_dict_normal(self): - """Test process_response_dict with stream=False (line 336)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor.process_response_dict_normal = MagicMock( - return_value={"text": "response"} - ) - processor.process_response_dict = ( - Ernie4_5_VLProcessor.process_response_dict.__get__( - processor, Ernie4_5_VLProcessor - ) - ) - + # Test with stream=False + processor.process_response_dict_normal = MagicMock(return_value={"text": "response"}) response_dict = {"ids": [1, 2, 3]} result = processor.process_response_dict(response_dict, stream=False) - processor.process_response_dict_normal.assert_called_once() self.assertEqual(result, {"text": "response"}) - def test_process_response_dict_enable_thinking_none(self): - """Test process_response_dict with enable_thinking=None (line 331)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor.process_response_dict_streaming = MagicMock( - return_value={"text": "response"} - ) - processor.process_response_dict = ( - Ernie4_5_VLProcessor.process_response_dict.__get__( - processor, Ernie4_5_VLProcessor - ) - ) - + # Test with enable_thinking=None (should default to True) + processor.process_response_dict_streaming = MagicMock(return_value={"text": "response"}) response_dict = {"ids": [1, 2, 3]} - processor.process_response_dict( - response_dict, stream=True, enable_thinking=None - ) + processor.process_response_dict(response_dict, stream=True, enable_thinking=None) + processor.process_response_dict_streaming.assert_called_once_with(response_dict, enable_thinking=True) - # enable_thinking should default to True when None - processor.process_response_dict_streaming.assert_called_once_with( - response_dict, enable_thinking=True - ) - - def test_apply_default_parameters_with_dict(self): - """Test _apply_default_parameters with dict request (lines 102-116)""" + def test_apply_default_parameters(self): + """Test _apply_default_parameters with dict and object request (lines 102-116)""" with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): processor = Ernie4_5_VLProcessor("model_path") processor.generation_config = MagicMock() processor.generation_config.top_p = 0.8 processor.generation_config.temperature = 0.9 - processor._apply_default_parameters = ( - Ernie4_5_VLProcessor._apply_default_parameters.__get__( - processor, Ernie4_5_VLProcessor - ) + processor._apply_default_parameters = Ernie4_5_VLProcessor._apply_default_parameters.__get__( + processor, Ernie4_5_VLProcessor ) + # Test with dict request request = {} result = processor._apply_default_parameters(request) self.assertEqual(result["top_p"], 0.8) self.assertEqual(result["temperature"], 0.9) - def test_apply_default_parameters_with_object(self): - """Test _apply_default_parameters with object request (lines 108-109)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor.generation_config = MagicMock() - processor.generation_config.top_p = 0.8 - processor._apply_default_parameters = ( - Ernie4_5_VLProcessor._apply_default_parameters.__get__( - processor, Ernie4_5_VLProcessor - ) - ) - + # Test with object request class MockRequest: def __init__(self): self.top_p = None @@ -794,13 +611,9 @@ class TestDataProcessorTargetMethods(unittest.TestCase): def setUp(self): self.mock_tokenizer = MagicMock(spec=Ernie4_5Tokenizer) self.mock_tokenizer.ignored_index = -100 - self.mock_tokenizer.convert_tokens_to_ids.side_effect = ( - self._mock_convert_tokens_to_ids - ) + self.mock_tokenizer.convert_tokens_to_ids.side_effect = self._mock_convert_tokens_to_ids self.mock_tokenizer.chat_template = "mock_template" - self.mock_tokenizer.apply_chat_template.return_value = ( - "User: Hello<|image@placeholder|>" - ) + self.mock_tokenizer.apply_chat_template.return_value = "User: Hello<|image@placeholder|>" def mock_load_tokenizer(dp_instance): dp_instance.tokenizer = self.mock_tokenizer @@ -811,9 +624,7 @@ def mock_load_tokenizer(dp_instance): side_effect=mock_load_tokenizer, autospec=True, ): - with patch.object( - AdaptiveImageProcessor, "from_pretrained" - ) as mock_image_preprocessor: + with patch.object(AdaptiveImageProcessor, "from_pretrained") as mock_image_preprocessor: mock_image_preprocessor.return_value = MagicMock() self.data_processor = DataProcessor( tokenizer_name="mock_tokenizer", @@ -830,9 +641,7 @@ def mock_load_tokenizer(dp_instance): "assistant": "Assistant: ", } self.data_processor.enable_processor_cache = False - self.data_processor.extract_mm_items = MagicMock( - return_value=([], [], [], [], None, [], []) - ) + self.data_processor.extract_mm_items = MagicMock(return_value=([], [], [], [], None, [], [])) def _mock_convert_tokens_to_ids(self, token): token_id_map = { @@ -863,9 +672,7 @@ def test_prompt_token_ids2outputs_only_prompt_token_ids(self): f"input_ids 涓嶅尮閰嶏細瀹為檯{outputs['input_ids']}锛岄鏈焄{test_prompt_token_ids}]", ) - self.assertEqual( - outputs["token_type_ids"], [IDS_TYPE_FLAG["text"]] * prompt_len - ) + self.assertEqual(outputs["token_type_ids"], [IDS_TYPE_FLAG["text"]] * prompt_len) expected_position_ids = [[i] * 3 for i in range(prompt_len)] self.assertEqual(outputs["position_ids"], expected_position_ids) @@ -903,9 +710,7 @@ def test_prompt_token_ids2outputs_with_messages_no_mm(self): self.assertEqual(outputs["input_ids"], test_prompt_token_ids) - self.assertEqual( - outputs["token_type_ids"], [IDS_TYPE_FLAG["text"]] * prompt_len - ) + self.assertEqual(outputs["token_type_ids"], [IDS_TYPE_FLAG["text"]] * prompt_len) expected_position_ids = [[i] * 3 for i in range(prompt_len)] self.assertEqual(outputs["position_ids"], expected_position_ids) @@ -927,9 +732,7 @@ def test_prompt_token_ids2outputs_add_image(self): "messages": [ { "role": "user", - "content": [ - {"type": "image_url", "image_url": mock_img, "uuid": "img_uuid"} - ], + "content": [{"type": "image_url", "image_url": mock_img, "uuid": "img_uuid"}], } ], } @@ -943,9 +746,7 @@ def test_prompt_token_ids2outputs_add_image(self): [{"type": "image", "data": mock_img}], ) mock_resize = (None, (2, 4)) - self.data_processor.image_preprocessor.get_smarted_resize.return_value = ( - mock_resize - ) + self.data_processor.image_preprocessor.get_smarted_resize.return_value = mock_resize mock_preprocess = { "pixel_values": np.random.randn(1, 16, 16, 3), "image_grid_thw": np.array([[2, 4]]), @@ -1041,9 +842,7 @@ def test_prompt_token_ids2outputs_add_video(self): "messages": [ { "role": "user", - "content": [ - {"type": "video_url", "video_url": frames, "uuid": "vid_uuid"} - ], + "content": [{"type": "video_url", "video_url": frames, "uuid": "vid_uuid"}], } ], } @@ -1068,9 +867,7 @@ def test_prompt_token_ids2outputs_add_video(self): } self.data_processor.image_preprocessor.preprocess.return_value = mock_preprocess outputs = self.data_processor.prompt_token_ids2outputs(request) - self.assertEqual( - outputs["input_ids"], [101, 1004, 1001, 1001, 1001, 1001, 1005, 102] - ) + self.assertEqual(outputs["input_ids"], [101, 1004, 1001, 1001, 1001, 1001, 1005, 102]) self.assertEqual( outputs["token_type_ids"], [ @@ -1101,18 +898,14 @@ def test_prompt_token_ids2outputs_add_processed_video(self): temporal_conv_size = self.data_processor.temporal_conv_size token_per_frame = (h // spatial_conv_size) * (w // spatial_conv_size) num_tokens = (t // temporal_conv_size) * token_per_frame - mock_frames_data = np.random.randn( - num_tokens * spatial_conv_size**2 * temporal_conv_size, 28, 28 - ) + mock_frames_data = np.random.randn(num_tokens * spatial_conv_size**2 * temporal_conv_size, 28, 28) mock_frames_cache = (mock_frames_data, {"thw": (t, h, w)}) request = { "prompt_token_ids": test_prompt_token_ids, "messages": [ { "role": "user", - "content": [ - {"type": "video", "data": mock_frames_cache, "uuid": "vid_uuid"} - ], + "content": [{"type": "video", "data": mock_frames_cache, "uuid": "vid_uuid"}], } ], } @@ -1126,9 +919,7 @@ def test_prompt_token_ids2outputs_add_processed_video(self): [{"type": "video", "data": mock_frames_cache}], ) outputs = self.data_processor.prompt_token_ids2outputs(request) - self.assertEqual( - outputs["input_ids"], [101, 1004, 1001, 1001, 1001, 1001, 1005, 102] - ) + self.assertEqual(outputs["input_ids"], [101, 1004, 1001, 1001, 1001, 1001, 1005, 102]) self.assertEqual( outputs["token_type_ids"], [ @@ -1162,9 +953,7 @@ def test_prompt_token_ids2outputs_add_image_token_len_mismatch(self): "messages": [ { "role": "user", - "content": [ - {"type": "image_url", "image_url": mock_img, "uuid": "img_uuid"} - ], + "content": [{"type": "image_url", "image_url": mock_img, "uuid": "img_uuid"}], } ], } @@ -1241,9 +1030,7 @@ def test_prompt_token_ids2outputs_add_video_token_len_mismatch(self): "messages": [ { "role": "user", - "content": [ - {"type": "video_url", "video_url": frames, "uuid": "vid_uuid"} - ], + "content": [{"type": "video_url", "video_url": frames, "uuid": "vid_uuid"}], } ], } @@ -1278,18 +1065,14 @@ def test_prompt_token_ids2outputs_add_processed_video_token_len_mismatch(self): temporal_conv_size = self.data_processor.temporal_conv_size num_tokens = 4 - mock_frames_data = np.random.randn( - num_tokens * spatial_conv_size**2 * temporal_conv_size, 28, 28 - ) + mock_frames_data = np.random.randn(num_tokens * spatial_conv_size**2 * temporal_conv_size, 28, 28) mock_frames_cache = (mock_frames_data, {"thw": (t, h, w)}) request = { "prompt_token_ids": test_prompt_token_ids, "messages": [ { "role": "user", - "content": [ - {"type": "video", "data": mock_frames_cache, "uuid": "vid_uuid"} - ], + "content": [{"type": "video", "data": mock_frames_cache, "uuid": "vid_uuid"}], } ], } From f01c80b5c076bce1200e50bae0aceaf8a98bc6a8 Mon Sep 17 00:00:00 2001 From: kesmeey <2561100473@qq.com> Date: Mon, 8 Dec 2025 21:09:58 +0800 Subject: [PATCH 3/6] improve coverage --- tests/input/test_ernie4_5_vl_processor.py | 1094 --------------------- tests/input/test_ernie_vl_processor.py | 491 ++++++++- 2 files changed, 489 insertions(+), 1096 deletions(-) delete mode 100644 tests/input/test_ernie4_5_vl_processor.py diff --git a/tests/input/test_ernie4_5_vl_processor.py b/tests/input/test_ernie4_5_vl_processor.py deleted file mode 100644 index 4f480f071a0..00000000000 --- a/tests/input/test_ernie4_5_vl_processor.py +++ /dev/null @@ -1,1094 +0,0 @@ -import unittest -from unittest.mock import MagicMock, patch - -import numpy as np - -from fastdeploy.input.ernie4_5_tokenizer import Ernie4_5Tokenizer -from fastdeploy.input.ernie4_5_vl_processor import Ernie4_5_VLProcessor -from fastdeploy.input.ernie4_5_vl_processor.image_preprocessor.image_preprocessor_adaptive import ( - AdaptiveImageProcessor, -) -from fastdeploy.input.ernie4_5_vl_processor.process import DataProcessor -from fastdeploy.input.utils import IDS_TYPE_FLAG - - -class TestErnie4_5_vl_ProcessorProcessResponseDictStreaming(unittest.TestCase): - def setUp(self): - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None) as mock_init: - self.processor = Ernie4_5_VLProcessor("model_path") - mock_init.side_effect = lambda *args, **kwargs: print(f"__init__ called with {args}, {kwargs}") - - self.processor.tokenizer = MagicMock() - self.processor.tokenizer.eos_token_id = 1 - self.processor.decode_status = {} - self.processor.reasoning_end_dict = {} - self.processor.tool_parser_dict = {} - self.processor.generation_config = MagicMock() - self.processor.eos_token_ids = [1] - self.processor.reasoning_parser = MagicMock() - self.processor._check_mm_limits = MagicMock() - self.processor.ernie4_5_processor = MagicMock() - self.processor.pack_outputs = MagicMock() - - def mock_ids2tokens(token_ids, task_id): - self.processor.decode_status[task_id] = "mock_decode_status" - return "delta_text", [2, 3], "previous_texts" - - self.processor.ids2tokens = mock_ids2tokens - - def mock_messages2ids(request, **kwargs): - if "chat_template" in kwargs: - return [1] - else: - return [0] - - def mock_apply_default_parameters(request): - return request - - self.processor._apply_default_parameters = mock_apply_default_parameters - - self.mock_reasoning_parser = MagicMock() - self.mock_reasoning_parser.__class__.__name__ = "ErnieX1ReasoningParser" - # self.mock_reasoning_parser.extract_reasoning_content_streaming.return_value = ("reasoning", "text") - self.processor.reasoning_parser = self.mock_reasoning_parser - - self.mock_tool_parser = MagicMock() - self.mock_tool_parser.extract_tool_calls_streaming.return_value = None - self.mock_tool_parser_obj = MagicMock() - self.mock_tool_parser_obj.return_value = self.mock_tool_parser - self.processor.tool_parser_obj = self.mock_tool_parser_obj - - def test_process_request_dict_with_options(self): - # Test with prompt_token_ids - enable_thinking defaults to True - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - # Test with prompt_token_ids and chat_template_kwargs - # Note: When prompt_token_ids is present, the code uses setdefault for enable_thinking - # and doesn't process chat_template_kwargs for enable_thinking - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"enable_thinking": True}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - # When prompt_token_ids is present, chat_template_kwargs enable_thinking is NOT processed - # The code uses setdefault which sets enable_thinking to True - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"enable_thinking": False}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - # Since prompt_token_ids branch uses setdefault, enable_thinking defaults to True - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "123"}}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - def test_parse_processor_kwargs(self): - """Test _parse_processor_kwargs with various inputs (lines 128-163)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor._parse_processor_kwargs = Ernie4_5_VLProcessor._parse_processor_kwargs.__get__( - processor, Ernie4_5_VLProcessor - ) - - # Test with valid kwargs - valid_kwargs = { - "spatial_conv_size": 14, - "temporal_conv_size": 2, - "image_min_pixels": 1000, - "image_max_pixels": 10000, - } - result = processor._parse_processor_kwargs(valid_kwargs) - self.assertEqual(result, valid_kwargs) - - # Test with invalid type (implementation catches exception and returns empty dict) - invalid_kwargs = {"spatial_conv_size": "invalid"} # Should be int - result = Ernie4_5_VLProcessor._parse_processor_kwargs(processor, invalid_kwargs) - self.assertEqual(result, {}) - - # Test with non-dict input (implementation catches exception and returns empty dict) - result = Ernie4_5_VLProcessor._parse_processor_kwargs(processor, "not a dict") - self.assertEqual(result, {}) - - # Test exception handling with None - with patch("fastdeploy.input.ernie4_5_vl_processor.ernie4_5_vl_processor.data_processor_logger"): - result = processor._parse_processor_kwargs(None) - self.assertEqual(result, {}) - - def test_parse_limits(self): - """Test _parse_limits with various inputs (lines 165-179)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor._parse_limits = Ernie4_5_VLProcessor._parse_limits.__get__(processor, Ernie4_5_VLProcessor) - - # Test with valid limits - valid_limits = {"image": 5, "video": 3} - result = processor._parse_limits(valid_limits) - self.assertEqual(result["image"], 5) - self.assertEqual(result["video"], 3) - self.assertEqual(result["audio"], 1) # Default value - - # Test with empty input (None) - result = processor._parse_limits(None) - self.assertEqual(result["image"], 1) - self.assertEqual(result["video"], 1) - self.assertEqual(result["audio"], 1) - - # Test with invalid type (implementation catches exception and returns default limits) - result = Ernie4_5_VLProcessor._parse_limits(processor, "not a dict") - self.assertEqual(result["image"], 1) - self.assertEqual(result["video"], 1) - self.assertEqual(result["audio"], 1) - - def test_check_mm_limits(self): - """Test _check_mm_limits with various inputs (lines 182-201)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor._check_mm_limits = Ernie4_5_VLProcessor._check_mm_limits.__get__(processor, Ernie4_5_VLProcessor) - - # Test with dict input (should not raise) - processor.limit_mm_per_prompt = {"image": 2, "video": 1} - mm_data = {"image": [1, 2], "video": [1]} - processor._check_mm_limits(mm_data) - - # Test with messages input (should not raise) - messages = [ - {"role": "user", "content": [{"type": "image", "data": "img1"}]}, - {"role": "user", "content": [{"type": "video", "data": "vid1"}]}, - ] - processor._check_mm_limits(messages) - - # Test when limit is exceeded (should raise ValueError) - processor.limit_mm_per_prompt = {"image": 1, "video": 1} - mm_data = {"image": [1, 2, 3], "video": []} # 3 images, limit is 1 - with self.assertRaises(ValueError) as context: - processor._check_mm_limits(mm_data) - self.assertIn("Too many image items", str(context.exception)) - - def test_process_request(self): - """Test process_request method (lines 120-126)""" - from fastdeploy.engine.request import Request - - # Mock the process_request_dict method - self.processor.process_request_dict = MagicMock() - - # Create a mock Request object - mock_request = MagicMock(spec=Request) - mock_request.to_dict.return_value = {"messages": [{"role": "user", "content": "Hello"}]} - - # Mock Request.from_dict to return a mock request - with patch.object(Request, "from_dict") as mock_from_dict: - mock_result_request = MagicMock(spec=Request) - mock_from_dict.return_value = mock_result_request - - self.processor.process_request(mock_request, max_model_len=100, chat_template_kwargs={"key": "value"}) - - # Verify to_dict was called - mock_request.to_dict.assert_called_once() - - # Verify process_request_dict was called - self.processor.process_request_dict.assert_called_once() - - # Verify from_dict was called - mock_from_dict.assert_called_once() - - def test_get_pad_id(self): - """Test get_pad_id method (line 86)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor.tokenizer = MagicMock() - processor.tokenizer.pad_token_id = 100 - processor.get_pad_id = Ernie4_5_VLProcessor.get_pad_id.__get__(processor, Ernie4_5_VLProcessor) - - result = processor.get_pad_id() - self.assertEqual(result, 100) - - def test_load_tokenizer(self): - """Test _load_tokenizer method (line 95)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - mock_tokenizer = MagicMock() - processor.ernie4_5_processor = MagicMock() - processor.ernie4_5_processor.tokenizer = mock_tokenizer - processor._load_tokenizer = Ernie4_5_VLProcessor._load_tokenizer.__get__(processor, Ernie4_5_VLProcessor) - - processor._load_tokenizer() - self.assertEqual(processor.tokenizer, mock_tokenizer) - - def test_process_request_dict_with_stop_sequences(self): - """Test process_request_dict with stop sequences (lines 212-214)""" - self.processor.update_stop_seq = MagicMock(return_value=([100, 101], [1, 1])) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "stop": ["stop1", "stop2"], - } - self.processor.process_request_dict(request_dict, 100) - - self.processor.update_stop_seq.assert_called_once_with(["stop1", "stop2"]) - self.assertEqual(request_dict["stop_token_ids"], [100, 101]) - self.assertEqual(request_dict["stop_seqs_len"], [1, 1]) - - def test_process_request_dict_with_bad_words(self): - """Test process_request_dict with bad words (lines 219-220)""" - self.processor.update_bad_words = MagicMock(return_value=[[200], [201]]) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "bad_words": ["bad1", "bad2"], - } - self.processor.process_request_dict(request_dict, 100) - - self.processor.update_bad_words.assert_called_once() - self.assertEqual(request_dict["bad_words_token_ids"], [[200], [201]]) - - def test_process_request_dict_with_prompt(self): - """Test process_request_dict with prompt (lines 228-235)""" - self.processor.ernie4_5_processor.text2ids = MagicMock( - return_value={ - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [], - "grid_thw": [], - "image_type_ids": [], - "cur_position": 3, - } - ) - - # Test with multimodal_data - request_dict = { - "prompt": "Hello world", - "multimodal_data": {"image": [], "video": []}, - } - self.processor.process_request_dict(request_dict, 100) - self.processor.ernie4_5_processor.text2ids.assert_called_once() - self.assertEqual(request_dict["prompt_tokens"], "Hello world") - - # Test without multimodal_data - should default to empty dict - self.processor.ernie4_5_processor.text2ids.reset_mock() - request_dict = { - "prompt": "Hello world", - } - self.processor.process_request_dict(request_dict, 100) - self.processor.ernie4_5_processor.text2ids.assert_called_once() - self.assertEqual(request_dict["prompt_tokens"], "Hello world") - - def test_process_request_dict_with_messages_only(self): - """Test process_request_dict with messages only (lines 236-259)""" - self.processor.ernie4_5_processor.request2ids = MagicMock( - return_value={ - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [], - "grid_thw": [], - "image_type_ids": [], - "cur_position": 3, - } - ) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - } - self.processor.process_request_dict(request_dict, 100) - - self.processor.ernie4_5_processor.request2ids.assert_called_once() - self.assertEqual(request_dict["enable_thinking"], True) - - def test_process_request_dict_messages_with_chat_template_kwargs(self): - """Test process_request_dict with messages and chat_template_kwargs (lines 243-245)""" - self.processor.ernie4_5_processor.request2ids = MagicMock( - return_value={ - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [], - "grid_thw": [], - "image_type_ids": [], - "cur_position": 3, - } - ) - - # Test that chat_template_kwargs values are copied to request - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": { - "custom_key": "custom_value", - "enable_thinking": False, - }, - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["custom_key"], "custom_value") - self.assertEqual(request_dict["enable_thinking"], False) - - def test_process_request_dict_messages_with_thinking_mode(self): - """Test process_request_dict with messages and different thinking_mode values (lines 248-255)""" - self.processor.ernie4_5_processor.request2ids = MagicMock( - return_value={ - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [], - "grid_thw": [], - "image_type_ids": [], - "cur_position": 3, - } - ) - - # Test thinking_mode = "close" (should set enable_thinking to False) - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], False) - - # Test thinking_mode = "false" (should set enable_thinking to False) - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], False) - - # Test thinking_mode = "open" (should set enable_thinking to True) - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - def test_process_request_dict_with_chat_template_kwargs_not_dict(self): - """Test process_request_dict with invalid chat_template_kwargs (line 247)""" - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": "not_a_dict", - } - - with self.assertRaises(ValueError) as context: - self.processor.process_request_dict(request_dict, 100) - self.assertIn("chat_template_kwargs must be a dict", str(context.exception)) - - def test_process_request_dict_no_valid_input(self): - """Test process_request_dict with no valid input (line 262)""" - request_dict = {} - - with self.assertRaises(ValueError) as context: - self.processor.process_request_dict(request_dict, 100) - self.assertIn("Request must contain", str(context.exception)) - - def test_process_request_dict_with_completion_token_ids(self): - """Test process_request_dict with completion_token_ids (line 264)""" - self.processor.append_completion_tokens = MagicMock() - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "completion_token_ids": [10, 11, 12], - } - self.processor.process_request_dict(request_dict, 100) - - self.processor.append_completion_tokens.assert_called_once() - - def test_process_request_dict_prompt_truncation(self): - """Test process_request_dict prompt truncation (line 275)""" - mock_outputs = MagicMock() - mock_outputs.__getitem__ = MagicMock( - side_effect=lambda k: { - "input_ids": np.array([1] * 150), - "token_type_ids": np.array([0] * 150), - "position_ids": np.array([[i, i, i] for i in range(150)]), - "images": None, - "grid_thw": None, - "image_type_ids": None, - "image_patch_id": 1001, - }.get(k) - ) - self.processor.pack_outputs = MagicMock(return_value=mock_outputs) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1] * 150, # 150 tokens - } - self.processor.process_request_dict(request_dict, 100) # max_model_len=100 - - # Should be truncated to max_model_len - 1 = 99 - self.assertEqual(len(request_dict["prompt_token_ids"]), 99) - - def test_process_request_dict_max_tokens_calculation(self): - """Test process_request_dict max_tokens calculation (lines 280, 286)""" - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "max_tokens": 200, # Larger than available - } - self.processor.process_request_dict(request_dict, 100) - - # max_tokens should be min(max_model_len - prompt_len, max_tokens) - self.assertLessEqual(request_dict["max_tokens"], 100 - 3) - - def test_process_request_dict_top_p_adjustment(self): - """Test process_request_dict top_p adjustment (line 288)""" - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "top_p": 1e-10, # Very small value - } - self.processor.process_request_dict(request_dict, 100) - - # top_p should be adjusted to _SAMPLING_EPS - self.assertGreaterEqual(request_dict["top_p"], 1e-5) - - def test_append_completion_tokens(self): - """Test append_completion_tokens method (lines 293-300)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor.append_completion_tokens = Ernie4_5_VLProcessor.append_completion_tokens.__get__( - processor, Ernie4_5_VLProcessor - ) - - multimodal_inputs = { - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "cur_position": 3, - } - completion_token_ids = [10, 11, 12] - - processor.append_completion_tokens(multimodal_inputs, completion_token_ids) - - self.assertEqual(multimodal_inputs["input_ids"], [1, 2, 3, 10, 11, 12]) - self.assertEqual(multimodal_inputs["token_type_ids"], [0, 0, 0, 0, 0, 0]) - self.assertEqual(len(multimodal_inputs["position_ids"]), 6) - self.assertEqual(multimodal_inputs["cur_position"], 6) - - def test_pack_outputs(self): - """Test pack_outputs with and without images (lines 304-319)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor.image_patch_id = 1001 - processor.pack_outputs = Ernie4_5_VLProcessor.pack_outputs.__get__(processor, Ernie4_5_VLProcessor) - - # Test with images - outs_with_images = { - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [np.array([[1, 2], [3, 4]])], - "grid_thw": [np.array([[1, 2, 2]])], - "image_type_ids": [0], - } - - result = processor.pack_outputs(outs_with_images) - self.assertIsNotNone(result["images"]) - self.assertIsNotNone(result["grid_thw"]) - self.assertIsNotNone(result["image_type_ids"]) - self.assertEqual(result["image_patch_id"], 1001) - self.assertIsInstance(result["input_ids"], np.ndarray) - self.assertIsInstance(result["token_type_ids"], np.ndarray) - self.assertIsInstance(result["position_ids"], np.ndarray) - - # Test without images - outs_without_images = { - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [], - "grid_thw": [], - "image_type_ids": [], - } - - result = processor.pack_outputs(outs_without_images) - self.assertIsNone(result["images"]) - self.assertIsNone(result["grid_thw"]) - self.assertIsNone(result["image_type_ids"]) - - def test_process_response_dict(self): - """Test process_response_dict with different parameters (lines 331-336)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor.process_response_dict = Ernie4_5_VLProcessor.process_response_dict.__get__( - processor, Ernie4_5_VLProcessor - ) - - # Test with stream=True - processor.process_response_dict_streaming = MagicMock(return_value={"text": "response"}) - response_dict = {"ids": [1, 2, 3]} - result = processor.process_response_dict(response_dict, stream=True) - processor.process_response_dict_streaming.assert_called_once() - self.assertEqual(result, {"text": "response"}) - - # Test with stream=False - processor.process_response_dict_normal = MagicMock(return_value={"text": "response"}) - response_dict = {"ids": [1, 2, 3]} - result = processor.process_response_dict(response_dict, stream=False) - processor.process_response_dict_normal.assert_called_once() - self.assertEqual(result, {"text": "response"}) - - # Test with enable_thinking=None (should default to True) - processor.process_response_dict_streaming = MagicMock(return_value={"text": "response"}) - response_dict = {"ids": [1, 2, 3]} - processor.process_response_dict(response_dict, stream=True, enable_thinking=None) - processor.process_response_dict_streaming.assert_called_once_with(response_dict, enable_thinking=True) - - def test_apply_default_parameters(self): - """Test _apply_default_parameters with dict and object request (lines 102-116)""" - with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): - processor = Ernie4_5_VLProcessor("model_path") - processor.generation_config = MagicMock() - processor.generation_config.top_p = 0.8 - processor.generation_config.temperature = 0.9 - processor._apply_default_parameters = Ernie4_5_VLProcessor._apply_default_parameters.__get__( - processor, Ernie4_5_VLProcessor - ) - - # Test with dict request - request = {} - result = processor._apply_default_parameters(request) - self.assertEqual(result["top_p"], 0.8) - self.assertEqual(result["temperature"], 0.9) - - # Test with object request - class MockRequest: - def __init__(self): - self.top_p = None - self.temperature = None - - def get(self, key): - return getattr(self, key, None) - - def set(self, key, value): - setattr(self, key, value) - - request = MockRequest() - result = processor._apply_default_parameters(request) - self.assertEqual(result.top_p, 0.8) - - -class TestDataProcessorTargetMethods(unittest.TestCase): - def setUp(self): - self.mock_tokenizer = MagicMock(spec=Ernie4_5Tokenizer) - self.mock_tokenizer.ignored_index = -100 - self.mock_tokenizer.convert_tokens_to_ids.side_effect = self._mock_convert_tokens_to_ids - self.mock_tokenizer.chat_template = "mock_template" - self.mock_tokenizer.apply_chat_template.return_value = "User: Hello<|image@placeholder|>" - - def mock_load_tokenizer(dp_instance): - dp_instance.tokenizer = self.mock_tokenizer - - with patch.object( - DataProcessor, - "_load_tokenizer", - side_effect=mock_load_tokenizer, - autospec=True, - ): - with patch.object(AdaptiveImageProcessor, "from_pretrained") as mock_image_preprocessor: - mock_image_preprocessor.return_value = MagicMock() - self.data_processor = DataProcessor( - tokenizer_name="mock_tokenizer", - image_preprocessor_name="mock_image_preprocessor", - enable_processor_cache=False, - ) - self.data_processor.image_patch_id = 1001 - self.data_processor.image_start_id = 1002 - self.data_processor.image_end_id = 1003 - self.data_processor.video_start_id = 1004 - self.data_processor.video_end_id = 1005 - self.data_processor.role_prefixes = { - "user": "User: ", - "assistant": "Assistant: ", - } - self.data_processor.enable_processor_cache = False - self.data_processor.extract_mm_items = MagicMock(return_value=([], [], [], [], None, [], [])) - - def _mock_convert_tokens_to_ids(self, token): - token_id_map = { - "<|begin_of_sentence|>": 101, - "<|end_of_sentence|>": 102, - "": 103, - "<|IMAGE_PLACEHOLDER|>": 1001, - "<|IMAGE_START|>": 1002, - "<|IMAGE_END|>": 1003, - "<|VIDEO_START|>": 1004, - "<|VIDEO_END|>": 1005, - } - return token_id_map.get(token, 999) - - def test_prompt_token_ids2outputs_only_prompt_token_ids(self): - test_prompt_token_ids = [101, 999, 998, 997, 102] - request = { - "prompt_token_ids": test_prompt_token_ids, - } - - outputs = self.data_processor.prompt_token_ids2outputs(request) - - prompt_len = len(test_prompt_token_ids) - - self.assertEqual( - outputs["input_ids"], - test_prompt_token_ids, - f"input_ids 涓嶅尮閰嶏細瀹為檯{outputs['input_ids']}锛岄鏈焄{test_prompt_token_ids}]", - ) - - self.assertEqual(outputs["token_type_ids"], [IDS_TYPE_FLAG["text"]] * prompt_len) - - expected_position_ids = [[i] * 3 for i in range(prompt_len)] - self.assertEqual(outputs["position_ids"], expected_position_ids) - - self.assertEqual(outputs["cur_position"], prompt_len) - - self.assertEqual(len(outputs["images"]), 0) - self.assertEqual(len(outputs["grid_thw"]), 0) - self.assertEqual(len(outputs["mm_positions"]), 0) - self.assertEqual(len(outputs["mm_hashes"]), 0) - self.assertEqual(outputs["video_cnt"], 0) - self.assertEqual(outputs["num_input_image_tokens"], 0) - self.assertEqual(outputs["num_input_video_tokens"], 0) - - def test_prompt_token_ids2outputs_with_messages_no_mm(self): - test_prompt_token_ids = [101, 999, 998, 997, 102] - request = { - "prompt_token_ids": test_prompt_token_ids, - "messages": [{"role": "user", "content": "Hello World"}], - } - - self.data_processor.extract_mm_items.return_value = ( - [], - [], - [], - [], - None, - [], - [], - ) - - outputs = self.data_processor.prompt_token_ids2outputs(request) - - prompt_len = len(test_prompt_token_ids) - - self.assertEqual(outputs["input_ids"], test_prompt_token_ids) - - self.assertEqual(outputs["token_type_ids"], [IDS_TYPE_FLAG["text"]] * prompt_len) - - expected_position_ids = [[i] * 3 for i in range(prompt_len)] - self.assertEqual(outputs["position_ids"], expected_position_ids) - - self.assertEqual(outputs["cur_position"], prompt_len) - - self.assertEqual(len(outputs["images"]), 0) - self.assertEqual(outputs["video_cnt"], 0) - self.assertEqual(outputs["num_input_image_tokens"], 0) - - def test_prompt_token_ids2outputs_add_image(self): - test_prompt_token_ids = [101, 1002, 1001, 1001, 1003, 102] - mock_img = MagicMock() - mock_img.height = 224 - mock_img.width = 224 - mock_img.convert.return_value = mock_img - request = { - "prompt_token_ids": test_prompt_token_ids, - "messages": [ - { - "role": "user", - "content": [{"type": "image_url", "image_url": mock_img, "uuid": "img_uuid"}], - } - ], - } - self.data_processor.extract_mm_items.return_value = ( - [mock_img], - [], - ["img_uuid"], - [], - None, - [], - [{"type": "image", "data": mock_img}], - ) - mock_resize = (None, (2, 4)) - self.data_processor.image_preprocessor.get_smarted_resize.return_value = mock_resize - mock_preprocess = { - "pixel_values": np.random.randn(1, 16, 16, 3), - "image_grid_thw": np.array([[2, 4]]), - } - self.data_processor.image_preprocessor.preprocess.return_value = mock_preprocess - # self.data_processor._compute_3d_positions = MagicMock(return_value=[[i]*3 for i in range(4)]) - outputs = self.data_processor.prompt_token_ids2outputs(request) - self.assertEqual(outputs["input_ids"], [101, 1002, 1001, 1001, 1003, 102]) - self.assertEqual( - outputs["token_type_ids"], - [ - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["image"], - IDS_TYPE_FLAG["image"], - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["text"], - ], - ) - self.assertEqual(len(outputs["position_ids"]), 6) - self.assertEqual(outputs["cur_position"], 6) - self.assertEqual(len(outputs["images"]), 1) - self.assertIsNotNone(outputs["images"][0]) - self.assertEqual(outputs["num_input_image_tokens"], 2) - self.assertEqual(len(outputs["mm_positions"]), 1) - self.assertEqual(len(outputs["mm_hashes"]), 1) - self.assertEqual(len(outputs["grid_thw"]), 1) - self.assertEqual(len(outputs["image_type_ids"]), 1) - - def test_prompt_token_ids2outputs_add_processed_image(self): - test_prompt_token_ids = [101, 1002, 1001, 1001, 1003, 102] - mock_img_data = np.random.randn(8, 28, 28) - mock_img_cache = (mock_img_data, {"thw": (1, 8, 8)}) - request = { - "prompt_token_ids": test_prompt_token_ids, - "messages": [ - { - "role": "user", - "content": [ - { - "type": "image_url", - "image_url": mock_img_cache, - "uuid": "img_uuid", - } - ], - } - ], - } - self.data_processor.extract_mm_items.return_value = ( - [mock_img_cache], - [], - ["img_uuid"], - [], - None, - [], - [{"type": "image", "data": mock_img_cache}], - ) - outputs = self.data_processor.prompt_token_ids2outputs(request) - self.assertEqual(outputs["input_ids"], [101, 1002, 1001, 1001, 1003, 102]) - self.assertEqual( - outputs["token_type_ids"], - [ - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["image"], - IDS_TYPE_FLAG["image"], - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["text"], - ], - ) - self.assertEqual(len(outputs["position_ids"]), 20) - self.assertEqual(outputs["cur_position"], 8) - self.assertEqual(len(outputs["images"]), 1) - self.assertIsNotNone(outputs["images"][0]) - self.assertEqual(len(outputs["mm_positions"]), 1) - self.assertEqual(outputs["mm_hashes"][0], "img_uuid") - self.assertEqual(len(outputs["grid_thw"]), 1) - self.assertEqual(len(outputs["image_type_ids"]), 1) - - def test_prompt_token_ids2outputs_add_video(self): - test_prompt_token_ids = [101, 1004, 1001, 1001, 1001, 1001, 1005, 102] - mock_frame1 = MagicMock() - mock_frame1.height = 224 - mock_frame1.width = 224 - mock_frame1.convert.return_value = mock_frame1 - mock_frame2 = MagicMock() - mock_frame2.height = 224 - mock_frame2.width = 224 - mock_frame2.convert.return_value = mock_frame2 - frames = [mock_frame1, mock_frame2] - request = { - "prompt_token_ids": test_prompt_token_ids, - "messages": [ - { - "role": "user", - "content": [{"type": "video_url", "video_url": frames, "uuid": "vid_uuid"}], - } - ], - } - self.data_processor.extract_mm_items.return_value = ( - [], - [frames], - [], - ["vid_uuid"], - None, - [], - [{"type": "video", "data": frames}], - ) - self.data_processor._load_and_process_video = MagicMock(return_value=frames) - patches_h, patches_w = 4, 4 - self.data_processor.image_preprocessor.get_smarted_resize.return_value = ( - None, - (patches_h, patches_w), - ) - mock_preprocess = { - "pixel_values_videos": np.random.randn(2, patches_h, patches_w, 3), - "video_grid_thw": np.array([[patches_h, patches_w]] * 2), - } - self.data_processor.image_preprocessor.preprocess.return_value = mock_preprocess - outputs = self.data_processor.prompt_token_ids2outputs(request) - self.assertEqual(outputs["input_ids"], [101, 1004, 1001, 1001, 1001, 1001, 1005, 102]) - self.assertEqual( - outputs["token_type_ids"], - [ - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["video"], - IDS_TYPE_FLAG["video"], - IDS_TYPE_FLAG["video"], - IDS_TYPE_FLAG["video"], - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["text"], - ], - ) - self.assertEqual(len(outputs["position_ids"]), 8) - self.assertEqual(outputs["cur_position"], 6) - self.assertEqual(len(outputs["images"]), 1) - self.assertIsNotNone(outputs["images"][0]) - self.assertEqual(len(outputs["mm_positions"]), 1) - self.assertEqual(outputs["mm_hashes"][0], "vid_uuid") - self.assertEqual(len(outputs["grid_thw"]), 1) - self.assertEqual(len(outputs["image_type_ids"]), 2) - self.assertEqual(outputs["num_input_video_tokens"], 4) - - def test_prompt_token_ids2outputs_add_processed_video(self): - test_prompt_token_ids = [101, 1004, 1001, 1001, 1001, 1001, 1005, 102] - t, h, w = 2, 4, 4 - spatial_conv_size = self.data_processor.spatial_conv_size - temporal_conv_size = self.data_processor.temporal_conv_size - token_per_frame = (h // spatial_conv_size) * (w // spatial_conv_size) - num_tokens = (t // temporal_conv_size) * token_per_frame - mock_frames_data = np.random.randn(num_tokens * spatial_conv_size**2 * temporal_conv_size, 28, 28) - mock_frames_cache = (mock_frames_data, {"thw": (t, h, w)}) - request = { - "prompt_token_ids": test_prompt_token_ids, - "messages": [ - { - "role": "user", - "content": [{"type": "video", "data": mock_frames_cache, "uuid": "vid_uuid"}], - } - ], - } - self.data_processor.extract_mm_items.return_value = ( - [], - [mock_frames_cache], - [], - ["vid_uuid"], - None, - [], - [{"type": "video", "data": mock_frames_cache}], - ) - outputs = self.data_processor.prompt_token_ids2outputs(request) - self.assertEqual(outputs["input_ids"], [101, 1004, 1001, 1001, 1001, 1001, 1005, 102]) - self.assertEqual( - outputs["token_type_ids"], - [ - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["video"], - IDS_TYPE_FLAG["video"], - IDS_TYPE_FLAG["video"], - IDS_TYPE_FLAG["video"], - IDS_TYPE_FLAG["text"], - IDS_TYPE_FLAG["text"], - ], - ) - self.assertEqual(len(outputs["position_ids"]), 8) - self.assertEqual(outputs["cur_position"], 6) - self.assertEqual(len(outputs["images"]), 1) - self.assertIsNotNone(outputs["images"][0]) - self.assertEqual(len(outputs["mm_positions"]), 1) - self.assertEqual(outputs["mm_hashes"][0], "vid_uuid") - self.assertEqual(len(outputs["grid_thw"]), 1) - self.assertEqual(len(outputs["image_type_ids"]), 2) - - def test_prompt_token_ids2outputs_add_image_token_len_mismatch(self): - test_prompt_token_ids = [101, 1002, 1001, 1001, 1001, 1003, 102] - mock_img = MagicMock() - mock_img.height = 224 - mock_img.width = 224 - mock_img.convert.return_value = mock_img - request = { - "prompt_token_ids": test_prompt_token_ids, - "messages": [ - { - "role": "user", - "content": [{"type": "image_url", "image_url": mock_img, "uuid": "img_uuid"}], - } - ], - } - self.data_processor.extract_mm_items.return_value = ( - [mock_img], - [], - ["img_uuid"], - [], - None, - [], - [{"type": "image", "data": mock_img}], - ) - patches_h, patches_w = 8, 8 - self.data_processor.image_preprocessor.get_smarted_resize.return_value = ( - None, - (patches_h, patches_w), - ) - mock_preprocess = { - "pixel_values": np.random.randn(1, patches_h, patches_w, 3), - "image_grid_thw": np.array([[patches_h, patches_w]]), - } - self.data_processor.image_preprocessor.preprocess.return_value = mock_preprocess - with self.assertRaises(ValueError) as ctx: - self.data_processor.prompt_token_ids2outputs(request) - self.assertIn("image tokens num not match the size", str(ctx.exception)) - - def test_prompt_token_ids2outputs_add_processed_image_token_len_mismatch(self): - test_prompt_token_ids = [101, 1002, 1001, 1001, 1003, 102] - spatial_conv_size = self.data_processor.spatial_conv_size - num_tokens = 4 - mock_img_data = np.random.randn(num_tokens * (spatial_conv_size**2), 28, 28) - mock_img_cache = (mock_img_data, {"thw": (1, 8, 8)}) - request = { - "prompt_token_ids": test_prompt_token_ids, - "messages": [ - { - "role": "user", - "content": [ - { - "type": "image_url", - "image_url": mock_img_cache, - "uuid": "img_uuid", - } - ], - } - ], - } - self.data_processor.extract_mm_items.return_value = ( - [mock_img_cache], - [], - ["img_uuid"], - [], - None, - [], - [{"type": "image", "data": mock_img_cache}], - ) - with self.assertRaises(ValueError) as ctx: - self.data_processor.prompt_token_ids2outputs(request) - self.assertIn("image tokens num not match the size", str(ctx.exception)) - - def test_prompt_token_ids2outputs_add_video_token_len_mismatch(self): - test_prompt_token_ids = [101, 1004, 1001, 1001, 1005, 102] - mock_frame1 = MagicMock() - mock_frame1.height = 224 - mock_frame1.width = 224 - mock_frame1.convert.return_value = mock_frame1 - mock_frame2 = MagicMock() - mock_frame2.height = 224 - mock_frame2.width = 224 - mock_frame2.convert.return_value = mock_frame2 - frames = [mock_frame1, mock_frame2] - request = { - "prompt_token_ids": test_prompt_token_ids, - "messages": [ - { - "role": "user", - "content": [{"type": "video_url", "video_url": frames, "uuid": "vid_uuid"}], - } - ], - } - self.data_processor.extract_mm_items.return_value = ( - [], - [frames], - [], - ["vid_uuid"], - None, - [], - [{"type": "video", "data": frames}], - ) - self.data_processor._load_and_process_video = MagicMock(return_value=frames) - patches_h, patches_w = 8, 8 - self.data_processor.image_preprocessor.get_smarted_resize.return_value = ( - None, - (patches_h, patches_w), - ) - mock_preprocess = { - "pixel_values_videos": np.random.randn(2, patches_h, patches_w, 3), - "video_grid_thw": np.array([[patches_h, patches_w]] * 2), - } - self.data_processor.image_preprocessor.preprocess.return_value = mock_preprocess - with self.assertRaises(ValueError) as ctx: - self.data_processor.prompt_token_ids2outputs(request) - self.assertIn("video tokens num not match the size", str(ctx.exception)) - - def test_prompt_token_ids2outputs_add_processed_video_token_len_mismatch(self): - test_prompt_token_ids = [101, 1004, 1001, 1005, 102] - t, h, w = 2, 8, 8 - spatial_conv_size = self.data_processor.spatial_conv_size - temporal_conv_size = self.data_processor.temporal_conv_size - - num_tokens = 4 - mock_frames_data = np.random.randn(num_tokens * spatial_conv_size**2 * temporal_conv_size, 28, 28) - mock_frames_cache = (mock_frames_data, {"thw": (t, h, w)}) - request = { - "prompt_token_ids": test_prompt_token_ids, - "messages": [ - { - "role": "user", - "content": [{"type": "video", "data": mock_frames_cache, "uuid": "vid_uuid"}], - } - ], - } - self.data_processor.extract_mm_items.return_value = ( - [], - [mock_frames_cache], - [], - ["vid_uuid"], - None, - [], - [{"type": "video", "data": mock_frames_cache}], - ) - with self.assertRaises(ValueError) as ctx: - self.data_processor.prompt_token_ids2outputs(request) - self.assertIn("video tokens num not match the size", str(ctx.exception)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/input/test_ernie_vl_processor.py b/tests/input/test_ernie_vl_processor.py index c0286b811c2..b149ef77ca1 100644 --- a/tests/input/test_ernie_vl_processor.py +++ b/tests/input/test_ernie_vl_processor.py @@ -120,6 +120,283 @@ def test_process_request_dict_with_options(self): self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], True) + def test_init(self): + """Test __init__ method""" + with patch("fastdeploy.input.ernie4_5_vl_processor.ernie4_5_vl_processor.data_processor_logger"): + mock_dp = MagicMock() + mock_dp.image_patch_id = 1001 + mock_dp.spatial_conv_size = 14 + mock_dp.tokenizer = MagicMock() + mock_dp.tokenizer.pad_token_id = 0 + mock_dp.eval = MagicMock() + + with patch("fastdeploy.input.ernie4_5_vl_processor.ernie4_5_vl_processor.DataProcessor") as mock_dp_class: + mock_dp_class.return_value = mock_dp + with patch( + "fastdeploy.input.ernie4_5_vl_processor.ernie4_5_vl_processor.GenerationConfig" + ) as mock_gen_config: + mock_gen_config.from_pretrained.return_value = MagicMock() + with patch("paddleformers.trl.llm_utils.get_eos_token_id") as mock_get_eos: + mock_get_eos.return_value = [1, 2] + + # Test normal initialization + mock_reasoning_parser_class = MagicMock() + processor = Ernie4_5_VLProcessor( + "model_path", + limit_mm_per_prompt={"image": 2, "video": 1}, + mm_processor_kwargs={"spatial_conv_size": 14}, + reasoning_parser_obj=lambda tokenizer: mock_reasoning_parser_class, + tool_parser_obj=MagicMock(), + enable_processor_cache=True, + ) + + self.assertEqual(processor.image_patch_id, 1001) + self.assertEqual(processor.spatial_conv_size, 14) + self.assertIsNotNone(processor.tokenizer) + self.assertIsNotNone(processor.generation_config) + self.assertEqual(processor.eos_token_ids, [1, 2]) + self.assertEqual(processor.limit_mm_per_prompt["image"], 2) + self.assertEqual(processor.limit_mm_per_prompt["video"], 1) + mock_dp.eval.assert_called_once() + + # Test with generation config exception + mock_gen_config.from_pretrained.side_effect = Exception("Config not found") + processor2 = Ernie4_5_VLProcessor("model_path") + self.assertIsNone(processor2.generation_config) + + # Test with reasoning_parser_obj + mock_reasoning_parser = MagicMock() + processor3 = Ernie4_5_VLProcessor( + "model_path", reasoning_parser_obj=lambda tokenizer: mock_reasoning_parser + ) + self.assertIsNotNone(processor3.reasoning_parser) + + def test_parse_processor_kwargs(self): + """Test _parse_processor_kwargs with various inputs""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor._parse_processor_kwargs = Ernie4_5_VLProcessor._parse_processor_kwargs.__get__( + processor, Ernie4_5_VLProcessor + ) + + # Test with valid kwargs + valid_kwargs = { + "spatial_conv_size": 14, + "temporal_conv_size": 2, + "image_min_pixels": 1000, + "image_max_pixels": 10000, + } + result = processor._parse_processor_kwargs(valid_kwargs) + self.assertEqual(result, valid_kwargs) + + # Test with invalid type (implementation catches exception and returns empty dict) + invalid_kwargs = {"spatial_conv_size": "invalid"} # Should be int + result = Ernie4_5_VLProcessor._parse_processor_kwargs(processor, invalid_kwargs) + self.assertEqual(result, {}) + + # Test with non-dict input (implementation catches exception and returns empty dict) + result = Ernie4_5_VLProcessor._parse_processor_kwargs(processor, "not a dict") + self.assertEqual(result, {}) + + # Test exception handling with None + with patch("fastdeploy.input.ernie4_5_vl_processor.ernie4_5_vl_processor.data_processor_logger"): + result = processor._parse_processor_kwargs(None) + self.assertEqual(result, {}) + + def test_parse_limits(self): + """Test _parse_limits with various inputs""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor._parse_limits = Ernie4_5_VLProcessor._parse_limits.__get__(processor, Ernie4_5_VLProcessor) + + # Test with valid limits + valid_limits = {"image": 5, "video": 3} + result = processor._parse_limits(valid_limits) + self.assertEqual(result["image"], 5) + self.assertEqual(result["video"], 3) + self.assertEqual(result["audio"], 1) # Default value + + # Test with empty input (None) + result = processor._parse_limits(None) + self.assertEqual(result["image"], 1) + self.assertEqual(result["video"], 1) + self.assertEqual(result["audio"], 1) + + # Test with invalid type (implementation catches exception and returns default limits) + result = Ernie4_5_VLProcessor._parse_limits(processor, "not a dict") + self.assertEqual(result["image"], 1) + self.assertEqual(result["video"], 1) + self.assertEqual(result["audio"], 1) + + def test_check_mm_limits(self): + """Test _check_mm_limits with various inputs""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor._check_mm_limits = Ernie4_5_VLProcessor._check_mm_limits.__get__(processor, Ernie4_5_VLProcessor) + + # Test with dict input (should not raise) + processor.limit_mm_per_prompt = {"image": 2, "video": 1} + mm_data = {"image": [1, 2], "video": [1]} + processor._check_mm_limits(mm_data) + + # Test with messages input (should not raise) + messages = [ + {"role": "user", "content": [{"type": "image", "data": "img1"}]}, + {"role": "user", "content": [{"type": "video", "data": "vid1"}]}, + ] + processor._check_mm_limits(messages) + + # Test when limit is exceeded (should raise ValueError) + processor.limit_mm_per_prompt = {"image": 1, "video": 1} + mm_data = {"image": [1, 2, 3], "video": []} # 3 images, limit is 1 + with self.assertRaises(ValueError) as context: + processor._check_mm_limits(mm_data) + self.assertIn("Too many image items", str(context.exception)) + + def test_process_request(self): + """Test process_request method""" + from fastdeploy.engine.request import Request + + # Mock the process_request_dict method + self.processor.process_request_dict = MagicMock() + + # Create a mock Request object + mock_request = MagicMock(spec=Request) + mock_request.to_dict.return_value = {"messages": [{"role": "user", "content": "Hello"}]} + + # Mock Request.from_dict to return a mock request + with patch.object(Request, "from_dict") as mock_from_dict: + mock_result_request = MagicMock(spec=Request) + mock_from_dict.return_value = mock_result_request + + self.processor.process_request(mock_request, max_model_len=100, chat_template_kwargs={"key": "value"}) + + # Verify to_dict was called + mock_request.to_dict.assert_called_once() + + # Verify process_request_dict was called + self.processor.process_request_dict.assert_called_once() + + # Verify from_dict was called + mock_from_dict.assert_called_once() + + def test_get_pad_id(self): + """Test get_pad_id method""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.tokenizer = MagicMock() + processor.tokenizer.pad_token_id = 100 + processor.get_pad_id = Ernie4_5_VLProcessor.get_pad_id.__get__(processor, Ernie4_5_VLProcessor) + + result = processor.get_pad_id() + self.assertEqual(result, 100) + + def test_load_tokenizer(self): + """Test _load_tokenizer method""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + mock_tokenizer = MagicMock() + processor.ernie4_5_processor = MagicMock() + processor.ernie4_5_processor.tokenizer = mock_tokenizer + processor._load_tokenizer = Ernie4_5_VLProcessor._load_tokenizer.__get__(processor, Ernie4_5_VLProcessor) + + processor._load_tokenizer() + self.assertEqual(processor.tokenizer, mock_tokenizer) + + def test_process_request_dict_with_stop_sequences(self): + """Test process_request_dict with stop sequences and bad words""" + # Test with stop sequences + self.processor.update_stop_seq = MagicMock(return_value=([100, 101], [1, 1])) + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "stop": ["stop1", "stop2"], + } + self.processor.process_request_dict(request_dict, 100) + self.processor.update_stop_seq.assert_called_once_with(["stop1", "stop2"]) + self.assertEqual(request_dict["stop_token_ids"], [100, 101]) + self.assertEqual(request_dict["stop_seqs_len"], [1, 1]) + + # Test with bad words + self.processor.update_bad_words = MagicMock(return_value=[[200], [201]]) + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "bad_words": ["bad1", "bad2"], + } + self.processor.process_request_dict(request_dict, 100) + self.processor.update_bad_words.assert_called_once() + self.assertEqual(request_dict["bad_words_token_ids"], [[200], [201]]) + + def test_process_request_dict_with_prompt(self): + """Test process_request_dict with prompt""" + self.processor.ernie4_5_processor.text2ids = MagicMock( + return_value={ + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + "cur_position": 3, + } + ) + + # Test with multimodal_data + request_dict = { + "prompt": "Hello world", + "multimodal_data": {"image": [], "video": []}, + } + self.processor.process_request_dict(request_dict, 100) + self.processor.ernie4_5_processor.text2ids.assert_called_once() + self.assertEqual(request_dict["prompt_tokens"], "Hello world") + + # Test without multimodal_data - should default to empty dict + self.processor.ernie4_5_processor.text2ids.reset_mock() + request_dict = { + "prompt": "Hello world", + } + self.processor.process_request_dict(request_dict, 100) + self.processor.ernie4_5_processor.text2ids.assert_called_once() + self.assertEqual(request_dict["prompt_tokens"], "Hello world") + + def test_process_request_dict_with_messages(self): + """Test process_request_dict with messages and various options""" + self.processor.ernie4_5_processor.request2ids = MagicMock( + return_value={ + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + "cur_position": 3, + } + ) + + # Test with messages only + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + } + self.processor.process_request_dict(request_dict, 100) + self.processor.ernie4_5_processor.request2ids.assert_called_once() + self.assertEqual(request_dict["enable_thinking"], True) + + # Test with chat_template_kwargs values copied to request + self.processor.ernie4_5_processor.request2ids.reset_mock() + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": { + "custom_key": "custom_value", + "enable_thinking": False, + }, + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["custom_key"], "custom_value") + self.assertEqual(request_dict["enable_thinking"], False) + + # Test thinking_mode = "close" (should set enable_thinking to False) + self.processor.ernie4_5_processor.request2ids.reset_mock() request_dict = { "messages": [{"role": "user", "content": "Hello"}], "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, @@ -127,6 +404,8 @@ def test_process_request_dict_with_options(self): self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], False) + # Test thinking_mode = "false" (should set enable_thinking to False) + self.processor.ernie4_5_processor.request2ids.reset_mock() request_dict = { "messages": [{"role": "user", "content": "Hello"}], "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, @@ -134,12 +413,220 @@ def test_process_request_dict_with_options(self): self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], False) + # Test thinking_mode = "open" (should set enable_thinking to True) + self.processor.ernie4_5_processor.request2ids.reset_mock() request_dict = { "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"enable_thinking": False}, + "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, } self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], False) + self.assertEqual(request_dict["enable_thinking"], True) + + def test_process_request_dict_with_chat_template_kwargs_not_dict(self): + """Test process_request_dict with invalid chat_template_kwargs""" + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "chat_template_kwargs": "not_a_dict", + } + + with self.assertRaises(ValueError) as context: + self.processor.process_request_dict(request_dict, 100) + self.assertIn("chat_template_kwargs must be a dict", str(context.exception)) + + def test_process_request_dict_no_valid_input(self): + """Test process_request_dict with no valid input""" + request_dict = {} + + with self.assertRaises(ValueError) as context: + self.processor.process_request_dict(request_dict, 100) + self.assertIn("Request must contain", str(context.exception)) + + def test_process_request_dict_with_completion_token_ids(self): + """Test process_request_dict with completion_token_ids""" + self.processor.append_completion_tokens = MagicMock() + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "completion_token_ids": [10, 11, 12], + } + self.processor.process_request_dict(request_dict, 100) + + self.processor.append_completion_tokens.assert_called_once() + + def test_process_request_dict_prompt_truncation(self): + """Test process_request_dict prompt truncation""" + mock_outputs = MagicMock() + mock_outputs.__getitem__ = MagicMock( + side_effect=lambda k: { + "input_ids": np.array([1] * 150), + "token_type_ids": np.array([0] * 150), + "position_ids": np.array([[i, i, i] for i in range(150)]), + "images": None, + "grid_thw": None, + "image_type_ids": None, + "image_patch_id": 1001, + }.get(k) + ) + self.processor.pack_outputs = MagicMock(return_value=mock_outputs) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1] * 150, # 150 tokens + } + self.processor.process_request_dict(request_dict, 100) # max_model_len=100 + + # Should be truncated to max_model_len - 1 = 99 + self.assertEqual(len(request_dict["prompt_token_ids"]), 99) + + def test_process_request_dict_max_tokens_calculation(self): + """Test process_request_dict max_tokens calculation""" + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "max_tokens": 200, # Larger than available + } + self.processor.process_request_dict(request_dict, 100) + + # max_tokens should be min(max_model_len - prompt_len, max_tokens) + self.assertLessEqual(request_dict["max_tokens"], 100 - 3) + + def test_process_request_dict_top_p_adjustment(self): + """Test process_request_dict top_p adjustment""" + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "top_p": 1e-10, # Very small value + } + self.processor.process_request_dict(request_dict, 100) + + # top_p should be adjusted to _SAMPLING_EPS + self.assertGreaterEqual(request_dict["top_p"], 1e-5) + + def test_append_completion_tokens(self): + """Test append_completion_tokens method""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.append_completion_tokens = Ernie4_5_VLProcessor.append_completion_tokens.__get__( + processor, Ernie4_5_VLProcessor + ) + + multimodal_inputs = { + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "cur_position": 3, + } + completion_token_ids = [10, 11, 12] + + processor.append_completion_tokens(multimodal_inputs, completion_token_ids) + + self.assertEqual(multimodal_inputs["input_ids"], [1, 2, 3, 10, 11, 12]) + self.assertEqual(multimodal_inputs["token_type_ids"], [0, 0, 0, 0, 0, 0]) + self.assertEqual(len(multimodal_inputs["position_ids"]), 6) + self.assertEqual(multimodal_inputs["cur_position"], 6) + + def test_pack_outputs(self): + """Test pack_outputs with and without images""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.image_patch_id = 1001 + processor.pack_outputs = Ernie4_5_VLProcessor.pack_outputs.__get__(processor, Ernie4_5_VLProcessor) + + # Test with images + outs_with_images = { + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [np.array([[1, 2], [3, 4]])], + "grid_thw": [np.array([[1, 2, 2]])], + "image_type_ids": [0], + } + + result = processor.pack_outputs(outs_with_images) + self.assertIsNotNone(result["images"]) + self.assertIsNotNone(result["grid_thw"]) + self.assertIsNotNone(result["image_type_ids"]) + self.assertEqual(result["image_patch_id"], 1001) + self.assertIsInstance(result["input_ids"], np.ndarray) + self.assertIsInstance(result["token_type_ids"], np.ndarray) + self.assertIsInstance(result["position_ids"], np.ndarray) + + # Test without images + outs_without_images = { + "input_ids": [1, 2, 3], + "token_type_ids": [0, 0, 0], + "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], + "images": [], + "grid_thw": [], + "image_type_ids": [], + } + + result = processor.pack_outputs(outs_without_images) + self.assertIsNone(result["images"]) + self.assertIsNone(result["grid_thw"]) + self.assertIsNone(result["image_type_ids"]) + + def test_process_response_dict(self): + """Test process_response_dict with different parameters""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.process_response_dict = Ernie4_5_VLProcessor.process_response_dict.__get__( + processor, Ernie4_5_VLProcessor + ) + + # Test with stream=True + processor.process_response_dict_streaming = MagicMock(return_value={"text": "response"}) + response_dict = {"ids": [1, 2, 3]} + result = processor.process_response_dict(response_dict, stream=True) + processor.process_response_dict_streaming.assert_called_once() + self.assertEqual(result, {"text": "response"}) + + # Test with stream=False + processor.process_response_dict_normal = MagicMock(return_value={"text": "response"}) + response_dict = {"ids": [1, 2, 3]} + result = processor.process_response_dict(response_dict, stream=False) + processor.process_response_dict_normal.assert_called_once() + self.assertEqual(result, {"text": "response"}) + + # Test with enable_thinking=None (should default to True) + processor.process_response_dict_streaming = MagicMock(return_value={"text": "response"}) + response_dict = {"ids": [1, 2, 3]} + processor.process_response_dict(response_dict, stream=True, enable_thinking=None) + processor.process_response_dict_streaming.assert_called_once_with(response_dict, enable_thinking=True) + + def test_apply_default_parameters(self): + """Test _apply_default_parameters with dict and object request""" + with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): + processor = Ernie4_5_VLProcessor("model_path") + processor.generation_config = MagicMock() + processor.generation_config.top_p = 0.8 + processor.generation_config.temperature = 0.9 + processor._apply_default_parameters = Ernie4_5_VLProcessor._apply_default_parameters.__get__( + processor, Ernie4_5_VLProcessor + ) + + # Test with dict request + request = {} + result = processor._apply_default_parameters(request) + self.assertEqual(result["top_p"], 0.8) + self.assertEqual(result["temperature"], 0.9) + + # Test with object request + class MockRequest: + def __init__(self): + self.top_p = None + self.temperature = None + + def get(self, key): + return getattr(self, key, None) + + def set(self, key, value): + setattr(self, key, value) + + request = MockRequest() + result = processor._apply_default_parameters(request) + self.assertEqual(result.top_p, 0.8) class TestDataProcessorTargetMethods(unittest.TestCase): From 25545f5e621d37175674c0487cce9f2fce972d4b Mon Sep 17 00:00:00 2001 From: kesmeey <2561100473@qq.com> Date: Tue, 9 Dec 2025 11:41:29 +0800 Subject: [PATCH 4/6] update --- tests/input/test_ernie_vl_processor.py | 159 +++++++++++++++---------- 1 file changed, 98 insertions(+), 61 deletions(-) diff --git a/tests/input/test_ernie_vl_processor.py b/tests/input/test_ernie_vl_processor.py index b149ef77ca1..4235e2f6dd7 100644 --- a/tests/input/test_ernie_vl_processor.py +++ b/tests/input/test_ernie_vl_processor.py @@ -1,4 +1,20 @@ -import unittest +""" +# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" + +import unittest from unittest.mock import MagicMock, patch import numpy as np @@ -13,7 +29,12 @@ from fastdeploy.input.utils import IDS_TYPE_FLAG -class TestErnie4_5_vl_ProcessorProcessResponseDictStreaming(unittest.TestCase): +class MockReasoningParser: + def get_model_status(self, prompt_token_ids): + return "think_start" + + +class TestErnie4_5VLProcessorProcessResponseDictStreaming(unittest.TestCase): def setUp(self): # Create mock object for Ernie4_5Processor instance with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None) as mock_init: @@ -23,38 +44,62 @@ def setUp(self): # Set necessary attributes self.processor.tokenizer = MagicMock() self.processor.tokenizer.eos_token_id = 1 - self.processor.decode_status = {} + self.processor.decode_status = {"test": []} self.processor.reasoning_end_dict = {} self.processor.tool_parser_dict = {} self.processor.generation_config = MagicMock() self.processor.eos_token_ids = [1] - self.processor.reasoning_parser = MagicMock() - self.processor._check_mm_limits = MagicMock() + self.processor.reasoning_parser = MockReasoningParser() + self.processor.model_status_dict = {"test": "think_start"} self.processor.ernie4_5_processor = MagicMock() - self.processor.pack_outputs = MagicMock() # Mock ids2tokens method def mock_ids2tokens(token_ids, task_id): - self.processor.decode_status[task_id] = "mock_decode_status" return "delta_text", [2, 3], "previous_texts" self.processor.ids2tokens = mock_ids2tokens - def mock_messages2ids(request, **kwargs): - if "chat_template" in kwargs: - return [1] - else: - return [0] + def mock_request2ids(request, **kwargs): + return {"input_ids": np.array([1, 2, 3]), "prompt_token_ids": [0]} + + def mock_check_mm_limits(item): + pass def mock_apply_default_parameters(request): return request + def mock_pack_outputs(outputs): + # Ensure input_ids is numpy array if it exists + result = outputs.copy() if isinstance(outputs, dict) else outputs + if isinstance(result, dict): + if "input_ids" in result and isinstance(result["input_ids"], list): + result["input_ids"] = np.array(result["input_ids"]) + if "token_type_ids" in result and isinstance(result["token_type_ids"], list): + result["token_type_ids"] = np.array(result["token_type_ids"]) + if "position_ids" in result and isinstance(result["position_ids"], list): + result["position_ids"] = np.array(result["position_ids"]) + return result + + def mock_prompt_token_ids2outputs(request): + return { + "input_ids": np.array([1, 1, 1]), + "token_type_ids": np.array([0, 0, 0]), + "position_ids": np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2]]), + "images": [], + "grid_thw": [], + "image_type_ids": [], + "cur_position": 3, + } + self.processor._apply_default_parameters = mock_apply_default_parameters + self.processor._check_mm_limits = mock_check_mm_limits + self.processor.ernie4_5_processor.request2ids = mock_request2ids + self.processor.ernie4_5_processor.prompt_token_ids2outputs = mock_prompt_token_ids2outputs + self.processor.pack_outputs = mock_pack_outputs # Mock reasoning parser self.mock_reasoning_parser = MagicMock() - self.mock_reasoning_parser.__class__.__name__ = "ErnieX1ReasoningParser" - # self.mock_reasoning_parser.extract_reasoning_content_streaming.return_value = ("reasoning", "text") + self.mock_reasoning_parser.extract_reasoning_content_streaming.return_value = None self.processor.reasoning_parser = self.mock_reasoning_parser # Mock tool parser @@ -64,6 +109,27 @@ def mock_apply_default_parameters(request): self.mock_tool_parser_obj.return_value = self.mock_tool_parser self.processor.tool_parser_obj = self.mock_tool_parser_obj + def test_think_status(self): + """测试 思考机制""" + request = { + "prompt": "hello", + "request_id": "test_1", + "prompt_token_ids": [1, 2, 3], + } + self.processor.reasoning_parser = MagicMock() + self.processor.reasoning_parser.get_model_status.return_value = "think_start" + self.processor.model_status_dict = {} + self.processor.process_request_dict(request, max_model_len=512) + self.assertEqual(request["enable_thinking"], True) + + request = { + "prompt": "hello", + "request_id": "test", + "prompt_token_ids": [1, 2, 3], + } + self.processor.process_request_dict(request, max_model_len=512) + self.assertEqual(request["enable_thinking"], True) + def test_process_request_dict_with_options(self): request_dict = { "messages": [{"role": "user", "content": "Hello"}], @@ -303,8 +369,8 @@ def test_load_tokenizer(self): processor._load_tokenizer() self.assertEqual(processor.tokenizer, mock_tokenizer) - def test_process_request_dict_with_stop_sequences(self): - """Test process_request_dict with stop sequences and bad words""" + def test_process_request_dict_comprehensive(self): + """Test process_request_dict with various scenarios""" # Test with stop sequences self.processor.update_stop_seq = MagicMock(return_value=([100, 101], [1, 1])) request_dict = { @@ -328,8 +394,7 @@ def test_process_request_dict_with_stop_sequences(self): self.processor.update_bad_words.assert_called_once() self.assertEqual(request_dict["bad_words_token_ids"], [[200], [201]]) - def test_process_request_dict_with_prompt(self): - """Test process_request_dict with prompt""" + # Test with prompt and multimodal_data self.processor.ernie4_5_processor.text2ids = MagicMock( return_value={ "input_ids": [1, 2, 3], @@ -341,8 +406,6 @@ def test_process_request_dict_with_prompt(self): "cur_position": 3, } ) - - # Test with multimodal_data request_dict = { "prompt": "Hello world", "multimodal_data": {"image": [], "video": []}, @@ -351,7 +414,7 @@ def test_process_request_dict_with_prompt(self): self.processor.ernie4_5_processor.text2ids.assert_called_once() self.assertEqual(request_dict["prompt_tokens"], "Hello world") - # Test without multimodal_data - should default to empty dict + # Test with prompt without multimodal_data self.processor.ernie4_5_processor.text2ids.reset_mock() request_dict = { "prompt": "Hello world", @@ -360,8 +423,7 @@ def test_process_request_dict_with_prompt(self): self.processor.ernie4_5_processor.text2ids.assert_called_once() self.assertEqual(request_dict["prompt_tokens"], "Hello world") - def test_process_request_dict_with_messages(self): - """Test process_request_dict with messages and various options""" + # Test with messages self.processor.ernie4_5_processor.request2ids = MagicMock( return_value={ "input_ids": [1, 2, 3], @@ -373,8 +435,6 @@ def test_process_request_dict_with_messages(self): "cur_position": 3, } ) - - # Test with messages only request_dict = { "messages": [{"role": "user", "content": "Hello"}], } @@ -395,7 +455,7 @@ def test_process_request_dict_with_messages(self): self.assertEqual(request_dict["custom_key"], "custom_value") self.assertEqual(request_dict["enable_thinking"], False) - # Test thinking_mode = "close" (should set enable_thinking to False) + # Test thinking_mode = "close" self.processor.ernie4_5_processor.request2ids.reset_mock() request_dict = { "messages": [{"role": "user", "content": "Hello"}], @@ -404,7 +464,7 @@ def test_process_request_dict_with_messages(self): self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], False) - # Test thinking_mode = "false" (should set enable_thinking to False) + # Test thinking_mode = "false" self.processor.ernie4_5_processor.request2ids.reset_mock() request_dict = { "messages": [{"role": "user", "content": "Hello"}], @@ -413,7 +473,7 @@ def test_process_request_dict_with_messages(self): self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], False) - # Test thinking_mode = "open" (should set enable_thinking to True) + # Test thinking_mode = "open" self.processor.ernie4_5_processor.request2ids.reset_mock() request_dict = { "messages": [{"role": "user", "content": "Hello"}], @@ -422,40 +482,32 @@ def test_process_request_dict_with_messages(self): self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], True) - def test_process_request_dict_with_chat_template_kwargs_not_dict(self): - """Test process_request_dict with invalid chat_template_kwargs""" + # Test invalid chat_template_kwargs request_dict = { "messages": [{"role": "user", "content": "Hello"}], "chat_template_kwargs": "not_a_dict", } - with self.assertRaises(ValueError) as context: self.processor.process_request_dict(request_dict, 100) self.assertIn("chat_template_kwargs must be a dict", str(context.exception)) - def test_process_request_dict_no_valid_input(self): - """Test process_request_dict with no valid input""" + # Test no valid input request_dict = {} - with self.assertRaises(ValueError) as context: self.processor.process_request_dict(request_dict, 100) self.assertIn("Request must contain", str(context.exception)) - def test_process_request_dict_with_completion_token_ids(self): - """Test process_request_dict with completion_token_ids""" + # Test with completion_token_ids self.processor.append_completion_tokens = MagicMock() - request_dict = { "messages": [{"role": "user", "content": "Hello"}], "prompt_token_ids": [1, 1, 1], "completion_token_ids": [10, 11, 12], } self.processor.process_request_dict(request_dict, 100) - self.processor.append_completion_tokens.assert_called_once() - def test_process_request_dict_prompt_truncation(self): - """Test process_request_dict prompt truncation""" + # Test prompt truncation mock_outputs = MagicMock() mock_outputs.__getitem__ = MagicMock( side_effect=lambda k: { @@ -469,38 +521,29 @@ def test_process_request_dict_prompt_truncation(self): }.get(k) ) self.processor.pack_outputs = MagicMock(return_value=mock_outputs) - request_dict = { "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1] * 150, # 150 tokens + "prompt_token_ids": [1] * 150, } - self.processor.process_request_dict(request_dict, 100) # max_model_len=100 - - # Should be truncated to max_model_len - 1 = 99 + self.processor.process_request_dict(request_dict, 100) self.assertEqual(len(request_dict["prompt_token_ids"]), 99) - def test_process_request_dict_max_tokens_calculation(self): - """Test process_request_dict max_tokens calculation""" + # Test max_tokens calculation request_dict = { "messages": [{"role": "user", "content": "Hello"}], "prompt_token_ids": [1, 1, 1], - "max_tokens": 200, # Larger than available + "max_tokens": 200, } self.processor.process_request_dict(request_dict, 100) - - # max_tokens should be min(max_model_len - prompt_len, max_tokens) self.assertLessEqual(request_dict["max_tokens"], 100 - 3) - def test_process_request_dict_top_p_adjustment(self): - """Test process_request_dict top_p adjustment""" + # Test top_p adjustment request_dict = { "messages": [{"role": "user", "content": "Hello"}], "prompt_token_ids": [1, 1, 1], - "top_p": 1e-10, # Very small value + "top_p": 1e-10, } self.processor.process_request_dict(request_dict, 100) - - # top_p should be adjusted to _SAMPLING_EPS self.assertGreaterEqual(request_dict["top_p"], 1e-5) def test_append_completion_tokens(self): @@ -589,12 +632,6 @@ def test_process_response_dict(self): processor.process_response_dict_normal.assert_called_once() self.assertEqual(result, {"text": "response"}) - # Test with enable_thinking=None (should default to True) - processor.process_response_dict_streaming = MagicMock(return_value={"text": "response"}) - response_dict = {"ids": [1, 2, 3]} - processor.process_response_dict(response_dict, stream=True, enable_thinking=None) - processor.process_response_dict_streaming.assert_called_once_with(response_dict, enable_thinking=True) - def test_apply_default_parameters(self): """Test _apply_default_parameters with dict and object request""" with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None): From 5fdbaf73f408146815eb32cafec3dce638c35e33 Mon Sep 17 00:00:00 2001 From: kesmeey <2561100473@qq.com> Date: Fri, 12 Dec 2025 12:16:52 +0800 Subject: [PATCH 5/6] fix: correct test expectation for thinking_mode false in test_ernie_vl_processor --- tests/input/test_ernie_vl_processor.py | 133 ++++++++++++++----------- 1 file changed, 73 insertions(+), 60 deletions(-) diff --git a/tests/input/test_ernie_vl_processor.py b/tests/input/test_ernie_vl_processor.py index 02101ce7f6c..579ebad7176 100644 --- a/tests/input/test_ernie_vl_processor.py +++ b/tests/input/test_ernie_vl_processor.py @@ -130,62 +130,6 @@ def test_think_status(self): self.processor.process_request_dict(request, max_model_len=512) self.assertEqual(request["enable_thinking"], True) - def test_process_request_dict_with_options(self): - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"enable_thinking": True}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"enable_thinking": False}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "123"}}, - "prompt_token_ids": [1, 1, 1], - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - def test_init(self): """Test __init__ method""" with patch("fastdeploy.input.ernie4_5_vl_processor.ernie4_5_vl_processor.data_processor_logger"): @@ -372,15 +316,16 @@ def test_load_tokenizer(self): def test_process_request_dict_comprehensive(self): """Test process_request_dict with various scenarios""" # Test with stop sequences - self.processor.update_stop_seq = MagicMock(return_value=([100, 101], [1, 1])) + self.processor.update_stop_seq = MagicMock(return_value=([[100], [101]], [1, 1])) request_dict = { "messages": [{"role": "user", "content": "Hello"}], "prompt_token_ids": [1, 1, 1], "stop": ["stop1", "stop2"], + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.processor.update_stop_seq.assert_called_once_with(["stop1", "stop2"]) - self.assertEqual(request_dict["stop_token_ids"], [100, 101]) + self.assertEqual(request_dict["stop_token_ids"], [[100], [101]]) self.assertEqual(request_dict["stop_seqs_len"], [1, 1]) # Test with bad words @@ -389,6 +334,7 @@ def test_process_request_dict_comprehensive(self): "messages": [{"role": "user", "content": "Hello"}], "prompt_token_ids": [1, 1, 1], "bad_words": ["bad1", "bad2"], + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.processor.update_bad_words.assert_called_once() @@ -409,6 +355,7 @@ def test_process_request_dict_comprehensive(self): request_dict = { "prompt": "Hello world", "multimodal_data": {"image": [], "video": []}, + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.processor.ernie4_5_processor.text2ids.assert_called_once() @@ -418,6 +365,7 @@ def test_process_request_dict_comprehensive(self): self.processor.ernie4_5_processor.text2ids.reset_mock() request_dict = { "prompt": "Hello world", + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.processor.ernie4_5_processor.text2ids.assert_called_once() @@ -437,6 +385,7 @@ def test_process_request_dict_comprehensive(self): ) request_dict = { "messages": [{"role": "user", "content": "Hello"}], + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.processor.ernie4_5_processor.request2ids.assert_called_once() @@ -450,6 +399,7 @@ def test_process_request_dict_comprehensive(self): "custom_key": "custom_value", "enable_thinking": False, }, + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["custom_key"], "custom_value") @@ -460,6 +410,7 @@ def test_process_request_dict_comprehensive(self): request_dict = { "messages": [{"role": "user", "content": "Hello"}], "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], False) @@ -469,18 +420,75 @@ def test_process_request_dict_comprehensive(self): request_dict = { "messages": [{"role": "user", "content": "Hello"}], "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, + "request_id": "test_1", } self.processor.reasoning_parser = MagicMock() self.processor.reasoning_parser.get_model_status.return_value = "think_start" self.processor.model_status_dict = {} - self.processor.process_request_dict(request, max_model_len=512) - self.assertEqual(request["enable_thinking"], True) + self.processor.process_request_dict(request_dict, max_model_len=512) + self.assertEqual(request_dict["enable_thinking"], False) # Test thinking_mode = "open" self.processor.ernie4_5_processor.request2ids.reset_mock() request_dict = { "messages": [{"role": "user", "content": "Hello"}], "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, + "request_id": "test_1", + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + # Test thinking_mode with prompt_token_ids (different code path) + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "chat_template_kwargs": {"enable_thinking": True}, + "request_id": "test_1", + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "chat_template_kwargs": {"enable_thinking": False}, + "request_id": "test_1", + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, + "request_id": "test_1", + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, + "request_id": "test_1", + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, + "request_id": "test_1", + } + self.processor.process_request_dict(request_dict, 100) + self.assertEqual(request_dict["enable_thinking"], True) + + request_dict = { + "messages": [{"role": "user", "content": "Hello"}], + "prompt_token_ids": [1, 1, 1], + "chat_template_kwargs": {"options": {"thinking_mode": "123"}}, + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.assertEqual(request_dict["enable_thinking"], True) @@ -489,6 +497,7 @@ def test_process_request_dict_comprehensive(self): request_dict = { "messages": [{"role": "user", "content": "Hello"}], "chat_template_kwargs": "not_a_dict", + "request_id": "test_1", } with self.assertRaises(ValueError) as context: self.processor.process_request_dict(request_dict, 100) @@ -506,6 +515,7 @@ def test_process_request_dict_comprehensive(self): "messages": [{"role": "user", "content": "Hello"}], "prompt_token_ids": [1, 1, 1], "completion_token_ids": [10, 11, 12], + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.processor.append_completion_tokens.assert_called_once() @@ -527,6 +537,7 @@ def test_process_request_dict_comprehensive(self): request_dict = { "messages": [{"role": "user", "content": "Hello"}], "prompt_token_ids": [1] * 150, + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.assertEqual(len(request_dict["prompt_token_ids"]), 99) @@ -536,6 +547,7 @@ def test_process_request_dict_comprehensive(self): "messages": [{"role": "user", "content": "Hello"}], "prompt_token_ids": [1, 1, 1], "max_tokens": 200, + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.assertLessEqual(request_dict["max_tokens"], 100 - 3) @@ -545,6 +557,7 @@ def test_process_request_dict_comprehensive(self): "messages": [{"role": "user", "content": "Hello"}], "prompt_token_ids": [1, 1, 1], "top_p": 1e-10, + "request_id": "test_1", } self.processor.process_request_dict(request_dict, 100) self.assertGreaterEqual(request_dict["top_p"], 1e-5) From 809accad07b38f6ebba90f5a01189a1039a7815e Mon Sep 17 00:00:00 2001 From: kesmeey <2561100473@qq.com> Date: Fri, 12 Dec 2025 19:31:48 +0800 Subject: [PATCH 6/6] remove test_process_request_dict_comprehensive test case --- tests/input/test_ernie_vl_processor.py | 249 ------------------------- 1 file changed, 249 deletions(-) diff --git a/tests/input/test_ernie_vl_processor.py b/tests/input/test_ernie_vl_processor.py index 579ebad7176..3f920ddaa7c 100644 --- a/tests/input/test_ernie_vl_processor.py +++ b/tests/input/test_ernie_vl_processor.py @@ -313,255 +313,6 @@ def test_load_tokenizer(self): processor._load_tokenizer() self.assertEqual(processor.tokenizer, mock_tokenizer) - def test_process_request_dict_comprehensive(self): - """Test process_request_dict with various scenarios""" - # Test with stop sequences - self.processor.update_stop_seq = MagicMock(return_value=([[100], [101]], [1, 1])) - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "stop": ["stop1", "stop2"], - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.processor.update_stop_seq.assert_called_once_with(["stop1", "stop2"]) - self.assertEqual(request_dict["stop_token_ids"], [[100], [101]]) - self.assertEqual(request_dict["stop_seqs_len"], [1, 1]) - - # Test with bad words - self.processor.update_bad_words = MagicMock(return_value=[[200], [201]]) - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "bad_words": ["bad1", "bad2"], - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.processor.update_bad_words.assert_called_once() - self.assertEqual(request_dict["bad_words_token_ids"], [[200], [201]]) - - # Test with prompt and multimodal_data - self.processor.ernie4_5_processor.text2ids = MagicMock( - return_value={ - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [], - "grid_thw": [], - "image_type_ids": [], - "cur_position": 3, - } - ) - request_dict = { - "prompt": "Hello world", - "multimodal_data": {"image": [], "video": []}, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.processor.ernie4_5_processor.text2ids.assert_called_once() - self.assertEqual(request_dict["prompt_tokens"], "Hello world") - - # Test with prompt without multimodal_data - self.processor.ernie4_5_processor.text2ids.reset_mock() - request_dict = { - "prompt": "Hello world", - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.processor.ernie4_5_processor.text2ids.assert_called_once() - self.assertEqual(request_dict["prompt_tokens"], "Hello world") - - # Test with messages - self.processor.ernie4_5_processor.request2ids = MagicMock( - return_value={ - "input_ids": [1, 2, 3], - "token_type_ids": [0, 0, 0], - "position_ids": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], - "images": [], - "grid_thw": [], - "image_type_ids": [], - "cur_position": 3, - } - ) - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.processor.ernie4_5_processor.request2ids.assert_called_once() - self.assertEqual(request_dict["enable_thinking"], True) - - # Test with chat_template_kwargs values copied to request - self.processor.ernie4_5_processor.request2ids.reset_mock() - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": { - "custom_key": "custom_value", - "enable_thinking": False, - }, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["custom_key"], "custom_value") - self.assertEqual(request_dict["enable_thinking"], False) - - # Test thinking_mode = "close" - self.processor.ernie4_5_processor.request2ids.reset_mock() - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], False) - - # Test thinking_mode = "false" - self.processor.ernie4_5_processor.request2ids.reset_mock() - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, - "request_id": "test_1", - } - self.processor.reasoning_parser = MagicMock() - self.processor.reasoning_parser.get_model_status.return_value = "think_start" - self.processor.model_status_dict = {} - self.processor.process_request_dict(request_dict, max_model_len=512) - self.assertEqual(request_dict["enable_thinking"], False) - - # Test thinking_mode = "open" - self.processor.ernie4_5_processor.request2ids.reset_mock() - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - # Test thinking_mode with prompt_token_ids (different code path) - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "chat_template_kwargs": {"enable_thinking": True}, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "chat_template_kwargs": {"enable_thinking": False}, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "chat_template_kwargs": {"options": {"thinking_mode": "open"}}, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "chat_template_kwargs": {"options": {"thinking_mode": "close"}}, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "chat_template_kwargs": {"options": {"thinking_mode": "false"}}, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "chat_template_kwargs": {"options": {"thinking_mode": "123"}}, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(request_dict["enable_thinking"], True) - - # Test invalid chat_template_kwargs - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "chat_template_kwargs": "not_a_dict", - "request_id": "test_1", - } - with self.assertRaises(ValueError) as context: - self.processor.process_request_dict(request_dict, 100) - self.assertIn("chat_template_kwargs must be a dict", str(context.exception)) - - # Test no valid input - request_dict = {} - with self.assertRaises(ValueError) as context: - self.processor.process_request_dict(request_dict, 100) - self.assertIn("Request must contain", str(context.exception)) - - # Test with completion_token_ids - self.processor.append_completion_tokens = MagicMock() - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "completion_token_ids": [10, 11, 12], - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.processor.append_completion_tokens.assert_called_once() - - # Test prompt truncation - mock_outputs = MagicMock() - mock_outputs.__getitem__ = MagicMock( - side_effect=lambda k: { - "input_ids": np.array([1] * 150), - "token_type_ids": np.array([0] * 150), - "position_ids": np.array([[i, i, i] for i in range(150)]), - "images": None, - "grid_thw": None, - "image_type_ids": None, - "image_patch_id": 1001, - }.get(k) - ) - self.processor.pack_outputs = MagicMock(return_value=mock_outputs) - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1] * 150, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertEqual(len(request_dict["prompt_token_ids"]), 99) - - # Test max_tokens calculation - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "max_tokens": 200, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertLessEqual(request_dict["max_tokens"], 100 - 3) - - # Test top_p adjustment - request_dict = { - "messages": [{"role": "user", "content": "Hello"}], - "prompt_token_ids": [1, 1, 1], - "top_p": 1e-10, - "request_id": "test_1", - } - self.processor.process_request_dict(request_dict, 100) - self.assertGreaterEqual(request_dict["top_p"], 1e-5) - def test_append_completion_tokens(self): """Test append_completion_tokens method""" with patch.object(Ernie4_5_VLProcessor, "__init__", return_value=None):