Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@

pytest.importorskip('anthropic')

from utils.anthropic_messages import get_async_anthropic_client_and_model
from utils.anthropic_messages import (
anthropic_extra_body,
get_async_anthropic_client_and_model,
)
from utils.constant import BACKEND_LIST, RESTFUL_MODEL_LIST


Expand All @@ -33,7 +36,7 @@ async def _sdk_simple_non_stream() -> object:
return await client.messages.create(
model=model_name,
max_tokens=1024,
temperature=0.01,
extra_body=anthropic_extra_body(temperature=0.01),
messages=[{'role': 'user', 'content': 'how are you!'}],
)

Expand All @@ -43,7 +46,7 @@ async def _sdk_system_non_stream() -> object:
return await client.messages.create(
model=model_name,
max_tokens=1024,
temperature=0.01,
extra_body=anthropic_extra_body(temperature=0.01),
system=[{'type': 'text', 'text': 'you are a helpful assistant'}],
messages=[{'role': 'user', 'content': 'how are you!'}],
)
Expand All @@ -54,7 +57,7 @@ async def _sdk_stream_events_and_final() -> tuple[list, object | None]:
stream = await client.messages.create(
model=model_name,
max_tokens=1024,
temperature=0.01,
extra_body=anthropic_extra_body(temperature=0.01),
messages=[{'role': 'user', 'content': 'how are you!'}],
stream=True,
)
Expand Down
21 changes: 11 additions & 10 deletions autotest/interface/restful/test_restful_anthropic_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
)
from utils.config_utils import get_config
from utils.constant import BACKEND_LIST, BASE_URL, RESTFUL_MODEL_LIST

from lmdeploy.serve.openai.api_client import APIClient
from utils.restful_return_check import (
build_session_sized_user_content,
get_client_and_model,
)

ANTHROPIC_VERSION = '2023-06-01'

Expand All @@ -41,7 +43,8 @@
def deployed_model_name() -> str:
"""Single model id exposed by the RESTFUL api_server."""

return APIClient(BASE_URL).available_models[0]
_, model_name = get_client_and_model(BASE_URL)
return model_name


@lru_cache(maxsize=1)
Expand Down Expand Up @@ -135,14 +138,9 @@ def _assert_count_tokens_json(data: dict) -> int:
return n


_LARGE_PAYLOAD_PREFIX = 'Reply with one word: OK. Context:\n'
_LARGE_PAYLOAD_MAX_TOKENS = 8


def _large_payload_user_content() -> str:
return f'{_LARGE_PAYLOAD_PREFIX}{"x" * (128 * 1024)}'


def _assert_anthropic_error_envelope(body: dict) -> dict:
assert body['type'] == 'error', body
err = body['error']
Expand Down Expand Up @@ -983,11 +981,14 @@ def test_count_tokens_empty_messages(self, backend, model_case, deployed_model_n
)
_assert_anthropic_invalid_request_error(resp)

def test_messages_large_user_payload(self, backend, model_case, deployed_model_name: str):
def test_messages_large_user_payload(self, backend, model_case, deployed_model_name: str, config):
"""Regression guard for large JSON bodies (CI-sized payload, not
stress-test scale)."""

user_content = _large_payload_user_content()
user_content = build_session_sized_user_content(
config=config, model_id=model_case,
max_completion_tokens=_LARGE_PAYLOAD_MAX_TOKENS,
)
resp = requests.post(
_MESSAGES_URL,
headers=_anthropic_headers(),
Expand Down
933 changes: 247 additions & 686 deletions autotest/interface/restful/test_restful_chat_completions_v1.py

Large diffs are not rendered by default.

263 changes: 151 additions & 112 deletions autotest/interface/restful/test_restful_completions_v1.py

Large diffs are not rendered by default.

42 changes: 24 additions & 18 deletions autotest/interface/restful/test_restful_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@
model_enables_return_routed_experts,
)
from utils.constant import BACKEND_LIST, BASE_URL, DEFAULT_MAX_COMPLETION_TOKENS, RESTFUL_MODEL_LIST
from utils.restful_return_check import cap_completion_tokens_for_session
from utils.toolkit import encode_text, parse_sse_stream

from lmdeploy.serve.openai.api_client import APIClient


@pytest.mark.parametrize('backend', BACKEND_LIST)
@pytest.mark.parametrize('model_name', RESTFUL_MODEL_LIST)
Expand Down Expand Up @@ -737,8 +736,8 @@ def test_input_ids_rejected(self):
self._post(payload)

response = exc_info.value.response
assert response.status_code in [400, 422], (f"Bad Request for case '{test_desc}', "
f'but got {response.status_code}')
assert response.status_code == 400, (f"Bad Request for case '{test_desc}', "
f'but got {response.status_code}')

def test_stress_concurrent_requests(self):
print(f'\n[Model: {self.model_name}] Running stress concurrent requests test')
Expand Down Expand Up @@ -952,21 +951,24 @@ def test_ignore_eos_parameter(self):
assert reason_ignore == 'length', \
f'ignore_eos=True must end due to length, actual: {reason_ignore}'

def test_max_tokens_default_cap_no_overshoot_followup(self):
def test_max_tokens_default_cap_no_overshoot_followup(self, config):
"""Hit DEFAULT max_tokens (8192) with ignore_eos; no overshoot; follow-
up must succeed.

Catches regressions where length-capped generation returns a few extra tokens and breaks the next request.
"""
print(f'\n[Model: {self.model_name}] Running max_tokens={DEFAULT_MAX_COMPLETION_TOKENS} '
prompt = 'Continue writing forever without stopping.'
max_tokens = cap_completion_tokens_for_session(
prompt, DEFAULT_MAX_COMPLETION_TOKENS,
config=config, model_id=self.model_name)
print(f'\n[Model: {self.model_name}] Running max_tokens={max_tokens} '
'length-cap / follow-up test')
max_tokens = DEFAULT_MAX_COMPLETION_TOKENS
# Align with existing generate/chat length checks (allow at most +1).
overshoot_slack = 1

resp = self._post(
{
'prompt': 'Continue writing forever without stopping.',
'prompt': prompt,
'max_tokens': max_tokens,
'ignore_eos': True,
'stream': False,
Expand Down Expand Up @@ -1026,10 +1028,11 @@ def test_skip_special_tokens(self, config):
assert not any(pattern in generated_text for pattern in special_patterns), \
'Expected no special pattern in the generated text but found one.'

def test_stop_token_ids(self):
def test_stop_token_ids(self, config):
print(f'\n[Model: {self.model_name}] Running stop_token_ids test')
api_client = APIClient(BASE_URL)
input_ids1, length1 = api_client.encode('.', add_bos=False)
model_path = get_model_path_from_config(config, self.model_name)
input_ids1 = encode_text(model_path, '.', add_special_tokens=False)
length1 = len(input_ids1)
print(f'input_ids1={input_ids1}, length1={length1}')

payload = {
Expand Down Expand Up @@ -1109,23 +1112,23 @@ def test_invalid_temperature_values(self):

with pytest.raises(requests.HTTPError) as exc_info:
self._post({'prompt': 'Test', 'max_tokens': 3, 'temperature': -0.5, 'stream': False})
assert exc_info.value.response.status_code in [400, 422]
assert exc_info.value.response.status_code == 400

print(' Invalid temperature values test passed')

def test_invalid_top_p_values(self):
print(f'\n[Model: {self.model_name}] Running invalid top_p values test')
with pytest.raises(requests.HTTPError) as exc_info:
self._post({'prompt': 'Test', 'max_tokens': 3, 'top_p': 1.5, 'stream': False})
assert exc_info.value.response.status_code in [400, 422]
assert exc_info.value.response.status_code == 400

print(' Invalid top_p values test passed')

def test_invalid_top_k_values(self):
print(f'\n[Model: {self.model_name}] Running invalid top_k values test')
with pytest.raises(requests.HTTPError) as exc_info:
self._post({'prompt': 'Test', 'max_tokens': 3, 'top_k': -5, 'stream': False})
assert exc_info.value.response.status_code in [400, 422]
assert exc_info.value.response.status_code == 400

print(' Invalid top_k values test passed')

Expand Down Expand Up @@ -1272,7 +1275,7 @@ def test_request_returns_experts(self, backend):

@pytest.mark.experts
@pytest.mark.not_turbomind
def test_request_returns_experts_max_tokens_cap_followup(self, backend):
def test_request_returns_experts_max_tokens_cap_followup(self, backend, config):
"""Hit DEFAULT max_tokens with return_routed_experts; length/experts
OK; follow-up OK.

Expand All @@ -1282,14 +1285,17 @@ def test_request_returns_experts_max_tokens_cap_followup(self, backend):
if not model_enables_return_routed_experts(
self.model_name, backend, required_suites=frozenset({'experts'})):
pytest.skip(ROUTED_EXPERTS_UNSUPPORTED_SKIP)
prompt = 'Continue writing forever without stopping.'
max_tokens = cap_completion_tokens_for_session(
prompt, DEFAULT_MAX_COMPLETION_TOKENS,
config=config, model_id=self.model_name)
print(f'\n[Model: {self.model_name}] Running experts max_tokens='
f'{DEFAULT_MAX_COMPLETION_TOKENS} length-cap / follow-up test')
max_tokens = DEFAULT_MAX_COMPLETION_TOKENS
f'{max_tokens} length-cap / follow-up test')
overshoot_slack = 1

resp = self._post(
{
'prompt': 'Continue writing forever without stopping.',
'prompt': prompt,
'max_tokens': max_tokens,
'ignore_eos': True,
'stream': False,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
from utils.config_utils import ROUTED_EXPERTS_UNSUPPORTED_SKIP
from utils.constant import DEFAULT_MAX_COMPLETION_TOKENS
from utils.restful_return_check import cap_completion_tokens_for_session
from utils.tool_reasoning_definitions import (
ALL_OPTIONAL_TOOL,
CALCULATOR_TOOL,
Expand Down Expand Up @@ -737,12 +738,15 @@ def test_streaming_routed_experts_max_tokens_cap_followup(self, backend, model_c
"""
if not self._validate_experts():
pytest.skip(ROUTED_EXPERTS_UNSUPPORTED_SKIP)
max_tokens = DEFAULT_MAX_COMPLETION_TOKENS
prompt = 'Continue writing forever without stopping.'
max_tokens = cap_completion_tokens_for_session(
prompt, DEFAULT_MAX_COMPLETION_TOKENS,
config=self._config, model_id=self._model_case)
overshoot_slack = 1
messages = [
{
'role': 'user',
'content': 'Continue writing forever without stopping.',
'content': prompt,
},
]
r = self._stream_tool_call_with_tokens(
Expand Down
Loading
Loading