main
py 154 lines 4.75 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import importlib.util
4 import io
5
6 import imagehash
7 import pybase64 as base64
8 import pytest
9 import requests
10 from PIL import Image
11
12 from tests.utils import RemoteOpenAIServer
13 from vllm.entrypoints.pooling.pooling.protocol import IOProcessorResponse
14
15 pytestmark = pytest.mark.skipif(
16 importlib.util.find_spec("terratorch") is None,
17 reason="terratorch unavailable while PyPI has `lightning` quarantined; see #41376",
18 )
19
20 models_config = {
21 "ibm-nasa-geospatial/Prithvi-EO-2.0-300M-TL-Sen1Floods11": {
22 "image_url": "https://huggingface.co/christian-pinto/Prithvi-EO-2.0-300M-TL-VLLM/resolve/main/valencia_example_2024-10-26.tiff", # noqa: E501
23 "out_hash": "aa6d92ad25926a5e",
24 "plugin": "prithvi_to_tiff",
25 },
26 "ibm-nasa-geospatial/Prithvi-EO-2.0-300M-BurnScars": {
27 "image_url": "https://huggingface.co/ibm-nasa-geospatial/Prithvi-EO-2.0-300M-BurnScars/resolve/main/examples/subsetted_512x512_HLS.S30.T10SEH.2018190.v1.4_merged.tif", # noqa: E501
28 "out_hash": "c07f4f602da73552",
29 "plugin": "prithvi_to_tiff",
30 },
31 }
32
33
34 def _compute_image_hash(base64_data: str) -> str:
35 # Decode the base64 output and create image from byte stream
36 decoded_image = base64.b64decode(base64_data)
37 image = Image.open(io.BytesIO(decoded_image))
38
39 # Compute perceptual hash of the output image
40 return str(imagehash.phash(image))
41
42
43 @pytest.fixture(scope="function")
44 def server(model_name, plugin):
45 args = [
46 "--runner",
47 "pooling",
48 "--enforce-eager",
49 "--skip-tokenizer-init",
50 # Limit the maximum number of parallel requests
51 # to avoid the model going OOM in CI.
52 "--max-num-seqs",
53 "32",
54 "--io-processor-plugin",
55 plugin,
56 "--enable-mm-embeds",
57 ]
58
59 with RemoteOpenAIServer(model_name, args) as remote_server:
60 yield remote_server
61
62
63 @pytest.mark.asyncio
64 @pytest.mark.parametrize(
65 "model_name, image_url, plugin, expected_hash",
66 [
67 (model_name, config["image_url"], config["plugin"], config["out_hash"])
68 for model_name, config in models_config.items()
69 ],
70 )
71 async def test_prithvi_mae_plugin_online(
72 server: RemoteOpenAIServer,
73 model_name: str,
74 image_url: str | dict,
75 plugin: str,
76 expected_hash: str,
77 ):
78 request_payload_url = {
79 "data": {
80 "data": image_url,
81 "data_format": "url",
82 "image_format": "tiff",
83 "out_data_format": "b64_json",
84 },
85 "priority": 0,
86 "model": model_name,
87 "softmax": False,
88 }
89
90 ret = requests.post(
91 server.url_for("pooling"),
92 json=request_payload_url,
93 )
94
95 response = ret.json()
96
97 # verify the request response is in the correct format
98 parsed_response: IOProcessorResponse = IOProcessorResponse(**response)
99 assert parsed_response
100
101 # verify the output is formatted as expected for this plugin
102 plugin_data = parsed_response.data
103 assert all(plugin_data.get(attr) for attr in ["type", "format", "data"])
104
105 # Compute the output image hash and compare it against the expected hash
106 image_hash = _compute_image_hash(plugin_data["data"])
107 assert image_hash == expected_hash, (
108 f"Image hash mismatch: expected {expected_hash}, got {image_hash}"
109 )
110
111
112 @pytest.mark.parametrize(
113 "model_name, image_url, plugin, expected_hash",
114 [
115 (model_name, config["image_url"], config["plugin"], config["out_hash"])
116 for model_name, config in models_config.items()
117 ],
118 )
119 def test_prithvi_mae_plugin_offline(
120 vllm_runner, model_name: str, image_url: str | dict, plugin: str, expected_hash: str
121 ):
122 img_data = dict(
123 data=image_url,
124 data_format="url",
125 image_format="tiff",
126 out_data_format="b64_json",
127 )
128
129 prompt = dict(data=img_data)
130
131 with vllm_runner(
132 model_name,
133 runner="pooling",
134 skip_tokenizer_init=True,
135 enable_mm_embeds=True,
136 enforce_eager=True,
137 # Limit the maximum number of parallel requests
138 # to avoid the model going OOM in CI.
139 max_num_seqs=32,
140 io_processor_plugin=plugin,
141 default_torch_num_threads=1,
142 ) as llm_runner:
143 pooler_output = llm_runner.get_llm().encode(prompt, pooling_task="plugin")
144
145 output = pooler_output[0].outputs
146
147 # verify the output is formatted as expected for this plugin
148 assert all(hasattr(output, attr) for attr in ["type", "format", "data"])
149
150 # Compute the output image hash and compare it against the expected hash
151 image_hash = _compute_image_hash(output.data)
152 assert image_hash == expected_hash, (
153 f"Image hash mismatch: expected {expected_hash}, got {image_hash}"
154 )