mirror of
https://github.com/hanzo-ml/kserve.git
synced 2026-10-30 20:53:49 +00:00
385 lines
12 KiB
Python
385 lines
12 KiB
Python
# Copyright 2024 The KServe Authors.
|
|
#
|
|
# 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 os
|
|
|
|
import pytest
|
|
from kubernetes import client
|
|
from kubernetes.client import V1ResourceRequirements
|
|
|
|
from kserve import (
|
|
V1beta1PredictorSpec,
|
|
V1beta1ModelSpec,
|
|
V1beta1ModelFormat,
|
|
V1beta1InferenceService,
|
|
V1beta1InferenceServiceSpec,
|
|
KServeClient,
|
|
)
|
|
from kserve.constants import constants
|
|
from ..common.utils import (
|
|
KSERVE_TEST_NAMESPACE,
|
|
generate,
|
|
rerank,
|
|
chat_completion_stream,
|
|
completion_stream,
|
|
)
|
|
|
|
# Cold model loads + pinned-revision HuggingFace pulls can outrun the 600s default
|
|
# on contended CI runners. Increase the timeout for all tests in this file.
|
|
ISVC_READY_TIMEOUT_S = 900
|
|
|
|
# Knative's per-revision progress deadline (cluster default 600s) decides when a
|
|
# slow-to-start Revision is permanently marked ``RevisionFailed``. Once that
|
|
# happens, no amount of polling in ``wait_isvc_ready`` will recover. KServe
|
|
# propagates this annotation onto the Knative revision template, so raising it
|
|
# here gives CPU vLLM init (model load + bfloat16 + KV-cache build) room to
|
|
# finish.
|
|
ISVC_ANNOTATIONS = {"serving.knative.dev/progress-deadline": "20m"}
|
|
|
|
|
|
@pytest.mark.vllm
|
|
def test_huggingface_vllm_cpu_openai_chat_completions():
|
|
service_name = "hf-qwen-chat-vllm"
|
|
predictor = V1beta1PredictorSpec(
|
|
min_replicas=1,
|
|
model=V1beta1ModelSpec(
|
|
model_format=V1beta1ModelFormat(
|
|
name="huggingface",
|
|
),
|
|
args=[
|
|
"--model_id",
|
|
"Qwen/Qwen2-0.5B-Instruct",
|
|
"--model_name",
|
|
"qwen-chat",
|
|
"--backend",
|
|
"vllm",
|
|
"--max_model_len",
|
|
"512",
|
|
"--dtype",
|
|
"bfloat16",
|
|
],
|
|
env=[
|
|
client.V1EnvVar(
|
|
name="VLLM_CPU_KVCACHE_SPACE",
|
|
value="1",
|
|
),
|
|
client.V1EnvVar(
|
|
name="VLLM_ENABLE_V1_MULTIPROCESSING",
|
|
value="0",
|
|
),
|
|
],
|
|
resources=V1ResourceRequirements(
|
|
requests={"cpu": "2", "memory": "7Gi"},
|
|
limits={"cpu": "2", "memory": "7Gi"},
|
|
),
|
|
),
|
|
)
|
|
|
|
isvc = V1beta1InferenceService(
|
|
api_version=constants.KSERVE_V1BETA1,
|
|
kind=constants.KSERVE_KIND_INFERENCESERVICE,
|
|
metadata=client.V1ObjectMeta(
|
|
name=service_name,
|
|
namespace=KSERVE_TEST_NAMESPACE,
|
|
annotations=ISVC_ANNOTATIONS,
|
|
),
|
|
spec=V1beta1InferenceServiceSpec(predictor=predictor),
|
|
)
|
|
|
|
kserve_client = KServeClient(
|
|
config_file=os.environ.get("KUBECONFIG", "~/.kube/config")
|
|
)
|
|
kserve_client.create(isvc)
|
|
kserve_client.wait_isvc_ready(
|
|
service_name,
|
|
namespace=KSERVE_TEST_NAMESPACE,
|
|
timeout_seconds=ISVC_READY_TIMEOUT_S,
|
|
)
|
|
|
|
res = generate(service_name, "./data/qwen_input_chat.json")
|
|
assert res["choices"][0]["message"]["content"] == "The result of 2 + 2 is 4."
|
|
|
|
kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE)
|
|
|
|
|
|
@pytest.mark.vllm
|
|
def test_huggingface_vllm_cpu_text_completion_streaming():
|
|
service_name = "hf-qwen-cmpl-stream-vllm"
|
|
predictor = V1beta1PredictorSpec(
|
|
min_replicas=1,
|
|
model=V1beta1ModelSpec(
|
|
model_format=V1beta1ModelFormat(
|
|
name="huggingface",
|
|
),
|
|
args=[
|
|
"--model_id",
|
|
"Qwen/Qwen2-0.5B",
|
|
"--model_name",
|
|
"qwen-cmpl-stream",
|
|
"--backend",
|
|
"vllm",
|
|
"--max_model_len",
|
|
"512",
|
|
"--dtype",
|
|
"bfloat16",
|
|
],
|
|
env=[
|
|
client.V1EnvVar(
|
|
name="VLLM_CPU_KVCACHE_SPACE",
|
|
value="1",
|
|
),
|
|
client.V1EnvVar(
|
|
name="VLLM_ENABLE_V1_MULTIPROCESSING",
|
|
value="0",
|
|
),
|
|
],
|
|
resources=V1ResourceRequirements(
|
|
requests={"cpu": "2", "memory": "7Gi"},
|
|
limits={"cpu": "2", "memory": "7Gi"},
|
|
),
|
|
),
|
|
)
|
|
|
|
isvc = V1beta1InferenceService(
|
|
api_version=constants.KSERVE_V1BETA1,
|
|
kind=constants.KSERVE_KIND_INFERENCESERVICE,
|
|
metadata=client.V1ObjectMeta(
|
|
name=service_name,
|
|
namespace=KSERVE_TEST_NAMESPACE,
|
|
annotations=ISVC_ANNOTATIONS,
|
|
),
|
|
spec=V1beta1InferenceServiceSpec(predictor=predictor),
|
|
)
|
|
|
|
kserve_client = KServeClient(
|
|
config_file=os.environ.get("KUBECONFIG", "~/.kube/config")
|
|
)
|
|
kserve_client.create(isvc)
|
|
kserve_client.wait_isvc_ready(
|
|
service_name,
|
|
namespace=KSERVE_TEST_NAMESPACE,
|
|
timeout_seconds=ISVC_READY_TIMEOUT_S,
|
|
)
|
|
|
|
full_response, _ = completion_stream(
|
|
service_name, "./data/qwen_input_cmpl_stream.json"
|
|
)
|
|
assert full_response.strip() == "The result of 2 + 2 is 4."
|
|
|
|
kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE)
|
|
|
|
|
|
@pytest.mark.vllm
|
|
def test_huggingface_vllm_cpu_openai_completions():
|
|
service_name = "hf-qwen-cmpl-vllm"
|
|
predictor = V1beta1PredictorSpec(
|
|
min_replicas=1,
|
|
model=V1beta1ModelSpec(
|
|
model_format=V1beta1ModelFormat(
|
|
name="huggingface",
|
|
),
|
|
args=[
|
|
"--model_id",
|
|
"Qwen/Qwen2-0.5B",
|
|
"--model_name",
|
|
"qwen-cmpl",
|
|
"--backend",
|
|
"vllm",
|
|
"--max_model_len",
|
|
"512",
|
|
"--dtype",
|
|
"bfloat16",
|
|
],
|
|
env=[
|
|
client.V1EnvVar(
|
|
name="VLLM_CPU_KVCACHE_SPACE",
|
|
value="1",
|
|
),
|
|
client.V1EnvVar(
|
|
name="VLLM_ENABLE_V1_MULTIPROCESSING",
|
|
value="0",
|
|
),
|
|
],
|
|
resources=V1ResourceRequirements(
|
|
requests={"cpu": "2", "memory": "7Gi"},
|
|
limits={"cpu": "2", "memory": "7Gi"},
|
|
),
|
|
),
|
|
)
|
|
|
|
isvc = V1beta1InferenceService(
|
|
api_version=constants.KSERVE_V1BETA1,
|
|
kind=constants.KSERVE_KIND_INFERENCESERVICE,
|
|
metadata=client.V1ObjectMeta(
|
|
name=service_name,
|
|
namespace=KSERVE_TEST_NAMESPACE,
|
|
annotations=ISVC_ANNOTATIONS,
|
|
),
|
|
spec=V1beta1InferenceServiceSpec(predictor=predictor),
|
|
)
|
|
|
|
kserve_client = KServeClient(
|
|
config_file=os.environ.get("KUBECONFIG", "~/.kube/config")
|
|
)
|
|
kserve_client.create(isvc)
|
|
kserve_client.wait_isvc_ready(
|
|
service_name,
|
|
namespace=KSERVE_TEST_NAMESPACE,
|
|
timeout_seconds=ISVC_READY_TIMEOUT_S,
|
|
)
|
|
res = generate(service_name, "./data/qwen_input_cmpl.json", chat_completions=False)
|
|
assert res["choices"][0]["text"].strip() == "The result of 2 + 2 is 4."
|
|
|
|
kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE)
|
|
|
|
|
|
@pytest.mark.vllm
|
|
def test_huggingface_vllm_openai_chat_completions_streaming():
|
|
service_name = "hf-qwen-chat-stream-vllm"
|
|
predictor = V1beta1PredictorSpec(
|
|
min_replicas=1,
|
|
model=V1beta1ModelSpec(
|
|
model_format=V1beta1ModelFormat(
|
|
name="huggingface",
|
|
),
|
|
args=[
|
|
"--model_id",
|
|
"Qwen/Qwen2-0.5B-Instruct",
|
|
"--model_name",
|
|
"qwen-chat-stream",
|
|
"--backend",
|
|
"vllm",
|
|
"--max_model_len",
|
|
"512",
|
|
"--dtype",
|
|
"bfloat16",
|
|
],
|
|
env=[
|
|
client.V1EnvVar(
|
|
name="VLLM_CPU_KVCACHE_SPACE",
|
|
value="1",
|
|
),
|
|
client.V1EnvVar(
|
|
name="VLLM_ENABLE_V1_MULTIPROCESSING",
|
|
value="0",
|
|
),
|
|
],
|
|
resources=V1ResourceRequirements(
|
|
requests={"cpu": "2", "memory": "7Gi"},
|
|
limits={"cpu": "2", "memory": "7Gi"},
|
|
),
|
|
),
|
|
)
|
|
|
|
isvc = V1beta1InferenceService(
|
|
api_version=constants.KSERVE_V1BETA1,
|
|
kind=constants.KSERVE_KIND_INFERENCESERVICE,
|
|
metadata=client.V1ObjectMeta(
|
|
name=service_name,
|
|
namespace=KSERVE_TEST_NAMESPACE,
|
|
annotations=ISVC_ANNOTATIONS,
|
|
),
|
|
spec=V1beta1InferenceServiceSpec(predictor=predictor),
|
|
)
|
|
|
|
kserve_client = KServeClient(
|
|
config_file=os.environ.get("KUBECONFIG", "~/.kube/config")
|
|
)
|
|
kserve_client.create(isvc)
|
|
kserve_client.wait_isvc_ready(
|
|
service_name,
|
|
namespace=KSERVE_TEST_NAMESPACE,
|
|
timeout_seconds=ISVC_READY_TIMEOUT_S,
|
|
)
|
|
|
|
full_response, _ = chat_completion_stream(
|
|
service_name, "./data/qwen_input_chat_stream.json"
|
|
)
|
|
assert full_response.strip() == "The result of 2 + 2 is 4."
|
|
|
|
kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE)
|
|
|
|
|
|
@pytest.mark.vllm
|
|
def test_huggingface_vllm_cpu_rerank():
|
|
service_name = "bge-reranker-base"
|
|
predictor = V1beta1PredictorSpec(
|
|
min_replicas=1,
|
|
model=V1beta1ModelSpec(
|
|
model_format=V1beta1ModelFormat(
|
|
name="huggingface",
|
|
),
|
|
args=[
|
|
"--model_id",
|
|
"BAAI/bge-reranker-base",
|
|
"--backend",
|
|
"vllm",
|
|
"--model_revision",
|
|
"2cfc18c9415c912f9d8155881c133215df768a70",
|
|
"--tokenizer_revision",
|
|
"2cfc18c9415c912f9d8155881c133215df768a70",
|
|
"--max-model-len",
|
|
"100",
|
|
"--dtype",
|
|
"bfloat16",
|
|
"--enforce-eager",
|
|
],
|
|
env=[
|
|
client.V1EnvVar(
|
|
name="VLLM_CPU_KVCACHE_SPACE",
|
|
value="1",
|
|
),
|
|
client.V1EnvVar(
|
|
name="VLLM_ENABLE_V1_MULTIPROCESSING",
|
|
value="0",
|
|
),
|
|
],
|
|
resources=V1ResourceRequirements(
|
|
requests={"cpu": "2", "memory": "6Gi"},
|
|
limits={"cpu": "2", "memory": "6Gi"},
|
|
),
|
|
),
|
|
)
|
|
|
|
isvc = V1beta1InferenceService(
|
|
api_version=constants.KSERVE_V1BETA1,
|
|
kind=constants.KSERVE_KIND_INFERENCESERVICE,
|
|
metadata=client.V1ObjectMeta(
|
|
name=service_name,
|
|
namespace=KSERVE_TEST_NAMESPACE,
|
|
annotations=ISVC_ANNOTATIONS,
|
|
),
|
|
spec=V1beta1InferenceServiceSpec(predictor=predictor),
|
|
)
|
|
|
|
kserve_client = KServeClient(
|
|
config_file=os.environ.get("KUBECONFIG", "~/.kube/config")
|
|
)
|
|
kserve_client.create(isvc)
|
|
kserve_client.wait_isvc_ready(
|
|
service_name,
|
|
namespace=KSERVE_TEST_NAMESPACE,
|
|
timeout_seconds=ISVC_READY_TIMEOUT_S,
|
|
)
|
|
|
|
res = rerank(service_name, "./data/bge-reranker-base.json")
|
|
assert res["results"][0]["index"] == 1
|
|
assert res["results"][0]["relevance_score"] == pytest.approx(1.0, rel=1e-2)
|
|
assert res["results"][0]["document"]["text"] == "The capital of France is Paris."
|
|
assert res["results"][1]["index"] == 0
|
|
assert res["results"][1]["relevance_score"] == pytest.approx(0.0, abs=1e-2)
|
|
assert res["results"][1]["document"]["text"] == "The capital of Brazil is Brasilia."
|
|
|
|
kserve_client.delete(service_name, KSERVE_TEST_NAMESPACE)
|