main
py 127 lines 3.91 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Run `pytest tests/entrypoints/openai/test_embedding_dimensions.py`."""
4
5 import openai
6 import pytest
7
8 from tests.conftest import HfRunner
9 from tests.models.language.pooling.embed_utils import (
10 float_embeddings,
11 run_embedding_correctness_test,
12 )
13 from tests.models.utils import EmbedModelInfo
14 from tests.utils import ROCM_EXTRA_ARGS, RemoteOpenAIServer
15 from vllm.entrypoints.pooling.embed.protocol import EmbeddingResponse
16
17 MODELS = [
18 EmbedModelInfo("intfloat/multilingual-e5-small", is_matryoshka=False),
19 EmbedModelInfo(
20 "Snowflake/snowflake-arctic-embed-m-v1.5",
21 is_matryoshka=True,
22 matryoshka_dimensions=[256],
23 ),
24 ]
25
26 input_texts = [
27 "The chef prepared a delicious meal.",
28 ]
29
30
31 @pytest.fixture(scope="module", params=MODELS)
32 def model_info(request):
33 return request.param
34
35
36 @pytest.fixture(scope="module", params=["bfloat16"])
37 def dtype(request):
38 return request.param
39
40
41 @pytest.fixture(scope="module")
42 def server(model_info, dtype: str):
43 args = [
44 "--runner",
45 "pooling",
46 # use half precision for speed and memory savings in CI environment
47 "--dtype",
48 dtype,
49 "--enforce-eager",
50 "--max-model-len",
51 "512",
52 ] + ROCM_EXTRA_ARGS
53
54 if model_info.name == "Snowflake/snowflake-arctic-embed-m-v1.5":
55 # Manually enable Matryoshka Embeddings
56 args.extend(
57 ["--trust_remote_code", "--hf_overrides", '{"matryoshka_dimensions":[256]}']
58 )
59
60 with RemoteOpenAIServer(model_info.name, args) as remote_server:
61 yield remote_server
62
63
64 @pytest.fixture(scope="module")
65 def hf_model(hf_runner, model_info, dtype: str):
66 with hf_runner(
67 model_info.name, dtype=dtype, is_sentence_transformer=True
68 ) as hf_model:
69 yield hf_model
70
71
72 @pytest.mark.asyncio
73 async def test_matryoshka(
74 model_info: EmbedModelInfo, server: RemoteOpenAIServer, hf_model: HfRunner
75 ):
76 client = server.get_async_client()
77
78 async def make_request_and_correctness_test(dimensions):
79 prompts = input_texts * 3
80
81 embedding_response = await client.embeddings.create(
82 model=model_info.name,
83 input=prompts,
84 dimensions=dimensions,
85 encoding_format="float",
86 )
87 embeddings = EmbeddingResponse.model_validate(
88 embedding_response.model_dump(mode="json")
89 )
90
91 assert embeddings.id is not None
92 assert len(embeddings.data) == 3
93 assert len(embeddings.data[0].embedding) > 0
94 assert embeddings.usage.completion_tokens == 0
95 assert embeddings.usage.prompt_tokens > 0
96 assert embeddings.usage.total_tokens > 0
97
98 if dimensions is not None:
99 assert len(embeddings.data[0].embedding) == dimensions
100
101 vllm_outputs = float_embeddings(embeddings)
102 run_embedding_correctness_test(hf_model, prompts, vllm_outputs, dimensions)
103
104 if model_info.is_matryoshka:
105 valid_dimensions: list[int | None] = [None]
106 if model_info.matryoshka_dimensions is not None:
107 valid_dimensions += model_info.matryoshka_dimensions[:2]
108
109 for dimensions in valid_dimensions:
110 await make_request_and_correctness_test(dimensions)
111
112 invalid_dimensions: list[int | None] = [-1]
113 if model_info.matryoshka_dimensions is not None:
114 assert 5 not in model_info.matryoshka_dimensions
115 invalid_dimensions.append(5)
116
117 for dimensions in invalid_dimensions:
118 with pytest.raises(openai.BadRequestError):
119 await make_request_and_correctness_test(dimensions)
120
121 else:
122 for dimensions in [None]:
123 await make_request_and_correctness_test(dimensions)
124
125 for dimensions in [-1, 16]:
126 with pytest.raises(openai.BadRequestError):
127 await make_request_and_correctness_test(dimensions)