[CI][RFC] Replace black-jupyter with ruff-format (#37210)
Co-authored-by: Alison Shao <a.shao@wustl.edu>
This commit is contained in:
co-authored by
Alison Shao
parent
2641e427be
commit
28262c20df
@@ -131,7 +131,6 @@ class MiniLoadBalancer:
|
||||
total=self.timeout
|
||||
) # Add timeout for request reliability
|
||||
) as session:
|
||||
|
||||
tasks = [
|
||||
session.post(f"{prefill_server}/{endpoint}", json=prefill_req),
|
||||
session.post(f"{decode_server}/{endpoint}", json=decode_req),
|
||||
@@ -141,7 +140,6 @@ class MiniLoadBalancer:
|
||||
prefill_response, decode_response = await asyncio.gather(*tasks)
|
||||
|
||||
if "return_logprob" in modified_request:
|
||||
|
||||
prefill_json = await prefill_response.json()
|
||||
ret_json = await decode_response.json()
|
||||
|
||||
|
||||
@@ -1069,7 +1069,6 @@ class RouterArgs:
|
||||
|
||||
prefill_urls = []
|
||||
for prefill_args in prefill_list:
|
||||
|
||||
url = prefill_args[0]
|
||||
|
||||
# Handle optional bootstrap port
|
||||
|
||||
@@ -788,7 +788,7 @@ def test_find_available_ports_and_wait_health(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
ls.time,
|
||||
"perf_counter",
|
||||
lambda: (base.__setitem__("t", base["t"] + 0.1) or base["t"]),
|
||||
lambda: base.__setitem__("t", base["t"] + 0.1) or base["t"],
|
||||
)
|
||||
|
||||
assert ls.wait_for_server_health("127.0.0.1", 12345, timeout=1)
|
||||
@@ -922,7 +922,6 @@ def test_launch_server_process_declares_on_a_resolved_record(monkeypatch):
|
||||
)
|
||||
|
||||
with patch("sglang_router.launch_router.logger") as mock_logger:
|
||||
|
||||
with pytest.raises(
|
||||
ValueError, match="PD disaggregation mode requires --prefill"
|
||||
):
|
||||
|
||||
@@ -120,9 +120,9 @@ class TestEnableThinking:
|
||||
if "content" in delta and delta["content"]:
|
||||
has_content = True
|
||||
|
||||
assert (
|
||||
has_reasoning
|
||||
), "The reasoning content is not included in the stream response"
|
||||
assert has_reasoning, (
|
||||
"The reasoning content is not included in the stream response"
|
||||
)
|
||||
assert has_content, "The stream response does not contain normal content"
|
||||
|
||||
def test_stream_chat_completion_without_reasoning(self, setup_backend):
|
||||
@@ -162,7 +162,7 @@ class TestEnableThinking:
|
||||
if "content" in delta and delta["content"]:
|
||||
has_content = True
|
||||
|
||||
assert (
|
||||
not has_reasoning
|
||||
), "The reasoning content should not be included in the stream response"
|
||||
assert not has_reasoning, (
|
||||
"The reasoning content should not be included in the stream response"
|
||||
)
|
||||
assert has_content, "The stream response does not contain normal content"
|
||||
|
||||
@@ -153,9 +153,9 @@ class TestOpenAIServerFunctionCalling:
|
||||
|
||||
tool_calls = response.choices[0].message.tool_calls
|
||||
|
||||
assert (
|
||||
isinstance(tool_calls, list) and len(tool_calls) > 0
|
||||
), "tool_calls should be a non-empty list"
|
||||
assert isinstance(tool_calls, list) and len(tool_calls) > 0, (
|
||||
"tool_calls should be a non-empty list"
|
||||
)
|
||||
|
||||
function_name = tool_calls[0].function.name
|
||||
assert function_name == "add", "Function name should be 'add'"
|
||||
@@ -221,20 +221,20 @@ class TestOpenAIServerFunctionCalling:
|
||||
if choice.delta.tool_calls:
|
||||
tool_call = choice.delta.tool_calls[0]
|
||||
if tool_call.function.name:
|
||||
assert (
|
||||
tool_call.function.name == "get_current_weather"
|
||||
), "Function name should be 'get_current_weather'"
|
||||
assert tool_call.function.name == "get_current_weather", (
|
||||
"Function name should be 'get_current_weather'"
|
||||
)
|
||||
found_function_name = True
|
||||
break
|
||||
|
||||
assert (
|
||||
found_function_name
|
||||
), "Target function name 'get_current_weather' was not found in the streaming chunks"
|
||||
assert found_function_name, (
|
||||
"Target function name 'get_current_weather' was not found in the streaming chunks"
|
||||
)
|
||||
|
||||
finish_reason = chunks[-1].choices[0].finish_reason
|
||||
assert (
|
||||
finish_reason == "tool_calls"
|
||||
), "Final response of function calling should have finish_reason 'tool_calls'"
|
||||
assert finish_reason == "tool_calls", (
|
||||
"Final response of function calling should have finish_reason 'tool_calls'"
|
||||
)
|
||||
|
||||
def test_function_calling_streaming_args_parsing(self, setup_backend):
|
||||
"""Test: Whether the function call arguments returned in streaming mode can be correctly concatenated into valid JSON.
|
||||
@@ -299,14 +299,14 @@ class TestOpenAIServerFunctionCalling:
|
||||
|
||||
assert function_name == "add", "Function name should be 'add'"
|
||||
joined_args = "".join(argument_fragments)
|
||||
assert (
|
||||
len(joined_args) > 0
|
||||
), "No parameter fragments were returned in the function call"
|
||||
assert len(joined_args) > 0, (
|
||||
"No parameter fragments were returned in the function call"
|
||||
)
|
||||
|
||||
finish_reason = chunks[-1].choices[0].finish_reason
|
||||
assert (
|
||||
finish_reason == "tool_calls"
|
||||
), "Final response of function calling should have finish_reason 'tool_calls'"
|
||||
assert finish_reason == "tool_calls", (
|
||||
"Final response of function calling should have finish_reason 'tool_calls'"
|
||||
)
|
||||
|
||||
# Check whether the concatenated JSON is valid
|
||||
try:
|
||||
@@ -445,21 +445,21 @@ class TestOpenAIServerFunctionCalling:
|
||||
arguments = tool_calls[0].function.arguments
|
||||
args_obj = json.loads(arguments)
|
||||
|
||||
assert (
|
||||
function_name == "get_weather"
|
||||
), f"Function name should be 'get_weather', got: {function_name}"
|
||||
assert (
|
||||
"city" in args_obj
|
||||
), f"Function arguments should have 'city', got: {args_obj}"
|
||||
assert function_name == "get_weather", (
|
||||
f"Function name should be 'get_weather', got: {function_name}"
|
||||
)
|
||||
assert "city" in args_obj, (
|
||||
f"Function arguments should have 'city', got: {args_obj}"
|
||||
)
|
||||
|
||||
# Make the test more robust by checking type and accepting valid responses
|
||||
city_value = args_obj["city"]
|
||||
assert isinstance(
|
||||
city_value, str
|
||||
), f"Parameter city should be a string, got: {type(city_value)}"
|
||||
assert (
|
||||
"Paris" in city_value or "France" in city_value
|
||||
), f"Parameter city should contain either 'Paris' or 'France', got: {city_value}"
|
||||
assert isinstance(city_value, str), (
|
||||
f"Parameter city should be a string, got: {type(city_value)}"
|
||||
)
|
||||
assert "Paris" in city_value or "France" in city_value, (
|
||||
f"Parameter city should contain either 'Paris' or 'France', got: {city_value}"
|
||||
)
|
||||
|
||||
def test_function_call_specific(self, setup_backend):
|
||||
"""Test: Whether tool_choice: ToolChoice works as expected.
|
||||
@@ -592,9 +592,9 @@ class TestOpenAIServerFunctionCalling:
|
||||
finish_reason_chunks[index].append(choice.finish_reason)
|
||||
|
||||
# Verify we got finish_reason chunks for both indices
|
||||
assert (
|
||||
len(finish_reason_chunks) == 2
|
||||
), f"Expected finish_reason chunks for 2 indices, got {len(finish_reason_chunks)}"
|
||||
assert len(finish_reason_chunks) == 2, (
|
||||
f"Expected finish_reason chunks for 2 indices, got {len(finish_reason_chunks)}"
|
||||
)
|
||||
|
||||
# Verify both index 0 and 1 have finish_reason
|
||||
assert 0 in finish_reason_chunks, "Missing finish_reason chunk for index 0"
|
||||
@@ -602,9 +602,9 @@ class TestOpenAIServerFunctionCalling:
|
||||
|
||||
# Verify the finish_reason is "tool_calls" since we forced tool calls
|
||||
for index, reasons in finish_reason_chunks.items():
|
||||
assert (
|
||||
reasons[-1] == "tool_calls"
|
||||
), f"Expected finish_reason 'tool_calls' for index {index}, got {reasons[-1]}"
|
||||
assert reasons[-1] == "tool_calls", (
|
||||
f"Expected finish_reason 'tool_calls' for index {index}, got {reasons[-1]}"
|
||||
)
|
||||
|
||||
def test_function_calling_streaming_no_tool_call(self, setup_backend):
|
||||
"""Test: Whether the finish_reason is stop in streaming mode when no tool call is given.
|
||||
@@ -663,14 +663,14 @@ class TestOpenAIServerFunctionCalling:
|
||||
found_tool_call = True
|
||||
break
|
||||
|
||||
assert (
|
||||
not found_tool_call
|
||||
), "Shouldn't have any tool_call in the streaming chunks"
|
||||
assert not found_tool_call, (
|
||||
"Shouldn't have any tool_call in the streaming chunks"
|
||||
)
|
||||
|
||||
finish_reason = chunks[-1].choices[0].finish_reason
|
||||
assert (
|
||||
finish_reason == "stop"
|
||||
), "Final response of no function calling should have finish_reason 'stop'"
|
||||
assert finish_reason == "stop", (
|
||||
"Final response of no function calling should have finish_reason 'stop'"
|
||||
)
|
||||
|
||||
def test_streaming_multiple_choices_without_tools(self, setup_backend):
|
||||
"""Test: Verify that each choice gets its own finish_reason chunk without tool calls.
|
||||
@@ -705,9 +705,9 @@ class TestOpenAIServerFunctionCalling:
|
||||
finish_reason_chunks[index].append(choice.finish_reason)
|
||||
|
||||
# Verify we got finish_reason chunks for both indices
|
||||
assert (
|
||||
len(finish_reason_chunks) == 2
|
||||
), f"Expected finish_reason chunks for 2 indices, got {len(finish_reason_chunks)}"
|
||||
assert len(finish_reason_chunks) == 2, (
|
||||
f"Expected finish_reason chunks for 2 indices, got {len(finish_reason_chunks)}"
|
||||
)
|
||||
|
||||
# Verify both index 0 and 1 have finish_reason
|
||||
assert 0 in finish_reason_chunks, "Missing finish_reason chunk for index 0"
|
||||
@@ -718,7 +718,9 @@ class TestOpenAIServerFunctionCalling:
|
||||
assert reasons[-1] in [
|
||||
"stop",
|
||||
"length",
|
||||
], f"Expected finish_reason 'stop' or 'length' for index {index}, got {reasons[-1]}"
|
||||
], (
|
||||
f"Expected finish_reason 'stop' or 'length' for index {index}, got {reasons[-1]}"
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -749,9 +751,9 @@ class TestOpenAIPythonicFunctionCalling:
|
||||
assert isinstance(tool_calls, list), "No tool_calls found"
|
||||
assert len(tool_calls) >= 1
|
||||
names = [tc.function.name for tc in tool_calls]
|
||||
assert (
|
||||
"get_weather" in names or "get_tourist_attractions" in names
|
||||
), f"Function name '{names}' should contain either 'get_weather' or 'get_tourist_attractions'"
|
||||
assert "get_weather" in names or "get_tourist_attractions" in names, (
|
||||
f"Function name '{names}' should contain either 'get_weather' or 'get_tourist_attractions'"
|
||||
)
|
||||
|
||||
def test_pythonic_tool_call_streaming(self, setup_backend):
|
||||
"""Test: Streaming pythonic tool call format; assert tool_call index is present."""
|
||||
@@ -782,7 +784,9 @@ class TestOpenAIPythonicFunctionCalling:
|
||||
assert found_index, "No index field found in any streamed tool_call"
|
||||
assert (
|
||||
"get_weather" in found_names or "get_tourist_attractions" in found_names
|
||||
), f"Function name '{found_names}' should contain either 'get_weather' or 'get_tourist_attractions'"
|
||||
), (
|
||||
f"Function name '{found_names}' should contain either 'get_weather' or 'get_tourist_attractions'"
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -1132,13 +1136,13 @@ class _TestToolChoiceBase:
|
||||
tool_call["id"] = tool_call_delta.id
|
||||
if tool_call_delta.function:
|
||||
if tool_call_delta.function.name:
|
||||
tool_call["function"][
|
||||
"name"
|
||||
] = tool_call_delta.function.name
|
||||
tool_call["function"]["name"] = (
|
||||
tool_call_delta.function.name
|
||||
)
|
||||
if tool_call_delta.function.arguments:
|
||||
tool_call["function"][
|
||||
"arguments"
|
||||
] += tool_call_delta.function.arguments
|
||||
tool_call["function"]["arguments"] += (
|
||||
tool_call_delta.function.arguments
|
||||
)
|
||||
|
||||
assert len(tool_calls_by_index) > 0
|
||||
|
||||
@@ -1271,9 +1275,9 @@ class _TestToolChoiceBase:
|
||||
assert len(tool_calls) == 2, f"Expected 2 tool calls, got {len(tool_calls)}"
|
||||
|
||||
called_functions = {call.function.name for call in tool_calls}
|
||||
assert (
|
||||
called_functions == expected_functions
|
||||
), f"Expected functions {expected_functions}, got {called_functions}"
|
||||
assert called_functions == expected_functions, (
|
||||
f"Expected functions {expected_functions}, got {called_functions}"
|
||||
)
|
||||
|
||||
def test_multi_tool_scenario_required(self, setup_backend):
|
||||
"""Test multi-tool scenario with tool_choice='required'."""
|
||||
@@ -1307,9 +1311,9 @@ class _TestToolChoiceBase:
|
||||
|
||||
if self._is_flaky_test("test_multi_tool_scenario_required"):
|
||||
# For flaky tests, just ensure basic functionality works
|
||||
assert (
|
||||
len(tool_calls) > 0
|
||||
), f"Expected at least 1 tool call, got {len(tool_calls)}"
|
||||
assert len(tool_calls) > 0, (
|
||||
f"Expected at least 1 tool call, got {len(tool_calls)}"
|
||||
)
|
||||
for call in tool_calls:
|
||||
assert call.function.name in available_names
|
||||
else:
|
||||
@@ -1317,9 +1321,9 @@ class _TestToolChoiceBase:
|
||||
assert len(tool_calls) == 2, f"Expected 2 tool calls, got {len(tool_calls)}"
|
||||
|
||||
called_functions = {call.function.name for call in tool_calls}
|
||||
assert (
|
||||
called_functions == expected_functions
|
||||
), f"Expected functions {expected_functions}, got {called_functions}"
|
||||
assert called_functions == expected_functions, (
|
||||
f"Expected functions {expected_functions}, got {called_functions}"
|
||||
)
|
||||
|
||||
def test_error_handling_invalid_tool_choice(self, setup_backend):
|
||||
"""Test error handling for invalid tool_choice."""
|
||||
|
||||
@@ -172,9 +172,9 @@ The SmartHome Mini is a compact smart home assistant available in black or white
|
||||
ret_num_top_logprobs = len(
|
||||
response.choices[0].logprobs.content[0].top_logprobs
|
||||
)
|
||||
assert (
|
||||
ret_num_top_logprobs == logprobs
|
||||
), f"{ret_num_top_logprobs} vs {logprobs}"
|
||||
assert ret_num_top_logprobs == logprobs, (
|
||||
f"{ret_num_top_logprobs} vs {logprobs}"
|
||||
)
|
||||
|
||||
assert len(response.choices) == parallel_sample_num
|
||||
assert response.choices[0].message.role == "assistant"
|
||||
@@ -223,9 +223,9 @@ The SmartHome Mini is a compact smart home assistant available in black or white
|
||||
data = response.choices[0].delta
|
||||
|
||||
if is_firsts.get(index, True):
|
||||
assert (
|
||||
data.role == "assistant"
|
||||
), "data.role was not 'assistant' for first chunk"
|
||||
assert data.role == "assistant", (
|
||||
"data.role was not 'assistant' for first chunk"
|
||||
)
|
||||
is_firsts[index] = False
|
||||
continue
|
||||
|
||||
@@ -240,9 +240,9 @@ The SmartHome Mini is a compact smart home assistant available in black or white
|
||||
ret_num_top_logprobs = len(
|
||||
response.choices[0].logprobs.content[0].top_logprobs
|
||||
)
|
||||
assert (
|
||||
ret_num_top_logprobs == logprobs
|
||||
), f"{ret_num_top_logprobs} vs {logprobs}"
|
||||
assert ret_num_top_logprobs == logprobs, (
|
||||
f"{ret_num_top_logprobs} vs {logprobs}"
|
||||
)
|
||||
|
||||
assert (
|
||||
isinstance(data.content, str)
|
||||
@@ -254,14 +254,14 @@ The SmartHome Mini is a compact smart home assistant available in black or white
|
||||
assert response.created
|
||||
|
||||
for index in range(parallel_sample_num):
|
||||
assert not is_firsts.get(
|
||||
index, True
|
||||
), f"index {index} is not found in the response"
|
||||
assert not is_firsts.get(index, True), (
|
||||
f"index {index} is not found in the response"
|
||||
)
|
||||
|
||||
for index in range(parallel_sample_num):
|
||||
assert (
|
||||
index in finish_reason_counts
|
||||
), f"No finish_reason found for index {index}"
|
||||
assert index in finish_reason_counts, (
|
||||
f"No finish_reason found for index {index}"
|
||||
)
|
||||
assert finish_reason_counts[index] == 1, (
|
||||
f"Expected 1 finish_reason chunk for index {index}, "
|
||||
f"got {finish_reason_counts[index]}"
|
||||
|
||||
@@ -91,9 +91,9 @@ class TestIgnoreEOS:
|
||||
# The ignore_eos response should either:
|
||||
# 1. Have more tokens than the default response (if default stopped at EOS before max_tokens)
|
||||
# 2. Have exactly max_tokens (if it reached the max_tokens limit)
|
||||
assert (
|
||||
ignore_eos_tokens > default_tokens or ignore_eos_tokens >= max_tokens
|
||||
), f"ignore_eos did not generate more tokens: {ignore_eos_tokens} vs {default_tokens}"
|
||||
assert ignore_eos_tokens > default_tokens or ignore_eos_tokens >= max_tokens, (
|
||||
f"ignore_eos did not generate more tokens: {ignore_eos_tokens} vs {default_tokens}"
|
||||
)
|
||||
|
||||
assert response_ignore_eos.choices[0].finish_reason == "length", (
|
||||
f"Expected finish_reason='length' for ignore_eos=True, "
|
||||
@@ -158,9 +158,9 @@ class TestLargeMaxNewTokens:
|
||||
# Verify all requests completed successfully
|
||||
assert len(responses) == num_requests
|
||||
for i, response in enumerate(responses):
|
||||
assert response.choices[
|
||||
0
|
||||
].message.content, f"Request {i} returned empty content"
|
||||
assert response.choices[0].message.content, (
|
||||
f"Request {i} returned empty content"
|
||||
)
|
||||
assert response.choices[0].finish_reason in ("stop", "length"), (
|
||||
f"Request {i} had unexpected finish_reason: "
|
||||
f"{response.choices[0].finish_reason}"
|
||||
|
||||
@@ -239,9 +239,9 @@ class TestEmbeddingCorrectness:
|
||||
|
||||
# Verify all similarities are close to 1.0
|
||||
for j, sim in enumerate(similarities):
|
||||
assert (
|
||||
abs(sim - 1.0) < tolerance
|
||||
), f"Set {i+1}, text {j+1}: similarity {sim:.4f} not close to 1.0"
|
||||
assert abs(sim - 1.0) < tolerance, (
|
||||
f"Set {i + 1}, text {j + 1}: similarity {sim:.4f} not close to 1.0"
|
||||
)
|
||||
|
||||
logger.info("Semantic similarity test set %d passed", i + 1)
|
||||
|
||||
@@ -273,8 +273,8 @@ class TestEmbeddingCorrectness:
|
||||
logger.info("Gateway relevance scores: %s", scores_gateway)
|
||||
logger.info("HF relevance scores: %s", scores_hf)
|
||||
|
||||
assert np.allclose(
|
||||
scores_gateway, scores_hf, atol=tolerance
|
||||
), f"Scores differ beyond tolerance:\nGateway: {scores_gateway}\nHF: {scores_hf}"
|
||||
assert np.allclose(scores_gateway, scores_hf, atol=tolerance), (
|
||||
f"Scores differ beyond tolerance:\nGateway: {scores_gateway}\nHF: {scores_hf}"
|
||||
)
|
||||
|
||||
logger.info("Relevance scores comparison passed")
|
||||
|
||||
@@ -300,17 +300,17 @@ class GPUMonitor:
|
||||
mean_threshold = thresholds.get("gpu_util_mean_min")
|
||||
if mean_threshold is not None:
|
||||
mean_value = overall.get("mean", 0.0)
|
||||
assert (
|
||||
mean_value >= mean_threshold
|
||||
), f"GPU utilization mean below threshold: {mean_value:.2f}% < {mean_threshold}%"
|
||||
assert mean_value >= mean_threshold, (
|
||||
f"GPU utilization mean below threshold: {mean_value:.2f}% < {mean_threshold}%"
|
||||
)
|
||||
|
||||
p50_threshold = thresholds.get("gpu_util_p50_min")
|
||||
if p50_threshold is not None:
|
||||
p50_value = overall.get("p50")
|
||||
if p50_value is not None:
|
||||
assert (
|
||||
p50_value >= p50_threshold
|
||||
), f"GPU utilization p50 below threshold: {p50_value:.2f}% < {p50_threshold}%"
|
||||
assert p50_value >= p50_threshold, (
|
||||
f"GPU utilization p50 below threshold: {p50_value:.2f}% < {p50_threshold}%"
|
||||
)
|
||||
|
||||
|
||||
def should_monitor(thresholds: dict[str, Any] | None) -> bool:
|
||||
|
||||
@@ -255,12 +255,12 @@ class TestClusterWideDiscovery:
|
||||
# A regression that quietly hardcoded a namespace filter would
|
||||
# still produce total=2 if labels happened to match elsewhere,
|
||||
# but only one of these IPs would surface.
|
||||
assert any(
|
||||
ip_a in u for u in urls
|
||||
), f"worker_a IP {ip_a} (ns {NAMESPACE}) not in {urls}"
|
||||
assert any(
|
||||
ip_b in u for u in urls
|
||||
), f"worker_b IP {ip_b} (ns {EXTRA_NAMESPACE}) not in {urls}"
|
||||
assert any(ip_a in u for u in urls), (
|
||||
f"worker_a IP {ip_a} (ns {NAMESPACE}) not in {urls}"
|
||||
)
|
||||
assert any(ip_b in u for u in urls), (
|
||||
f"worker_b IP {ip_b} (ns {EXTRA_NAMESPACE}) not in {urls}"
|
||||
)
|
||||
finally:
|
||||
_safe_delete_pod(worker_a, NAMESPACE)
|
||||
_safe_delete_pod(worker_b, EXTRA_NAMESPACE)
|
||||
|
||||
@@ -256,9 +256,9 @@ class TestGatewayRestart:
|
||||
"-l",
|
||||
"app=smg-gateway-restart",
|
||||
)
|
||||
assert res.get(
|
||||
"items"
|
||||
), "No pods found for selector app=smg-gateway-restart"
|
||||
assert res.get("items"), (
|
||||
"No pods found for selector app=smg-gateway-restart"
|
||||
)
|
||||
old_pod = res["items"][0]["metadata"]["name"]
|
||||
_kubectl(
|
||||
"delete",
|
||||
@@ -304,9 +304,9 @@ class TestGatewayRestart:
|
||||
logger.info("Workers after restart: %s", urls_after)
|
||||
|
||||
# No duplicates: each pod should appear exactly once.
|
||||
assert len(urls_after) == len(
|
||||
set(urls_after)
|
||||
), f"Duplicate worker registrations after gateway restart: {urls_after}"
|
||||
assert len(urls_after) == len(set(urls_after)), (
|
||||
f"Duplicate worker registrations after gateway restart: {urls_after}"
|
||||
)
|
||||
# Set equality: the same workers come back, neither dropped
|
||||
# nor duplicated.
|
||||
assert set(urls_after) == set(urls_before), (
|
||||
@@ -364,9 +364,9 @@ class TestPodIpChange:
|
||||
|
||||
ip_before = _get_pod_ip(pod_name)
|
||||
urls_before = {w["url"] for w in _get_workers(gateway_url)["workers"]}
|
||||
assert any(
|
||||
ip_before in url for url in urls_before
|
||||
), f"Expected initial worker URL containing {ip_before}, got {urls_before}"
|
||||
assert any(ip_before in url for url in urls_before), (
|
||||
f"Expected initial worker URL containing {ip_before}, got {urls_before}"
|
||||
)
|
||||
logger.info("Pod IP before: %s, urls: %s", ip_before, urls_before)
|
||||
|
||||
# Force-delete and wait until the registry no longer references
|
||||
@@ -384,9 +384,11 @@ class TestPodIpChange:
|
||||
)
|
||||
_wait_for_pod_gone(pod_name)
|
||||
_poll_until(
|
||||
lambda: not any(
|
||||
ip_before in w["url"]
|
||||
for w in _get_workers(gateway_url).get("workers", [])
|
||||
lambda: (
|
||||
not any(
|
||||
ip_before in w["url"]
|
||||
for w in _get_workers(gateway_url).get("workers", [])
|
||||
)
|
||||
),
|
||||
f"stale worker for IP {ip_before} removed",
|
||||
timeout=RECONCILIATION_WAIT_SECS,
|
||||
@@ -431,9 +433,9 @@ class TestPodIpChange:
|
||||
f"Expected exactly one worker URL containing current IP "
|
||||
f"{ip_after}, got {matching_after} (all urls: {urls_after})"
|
||||
)
|
||||
assert not any(
|
||||
ip_before in u for u in urls_after
|
||||
), f"Stale URL with old IP {ip_before} still in registry: {urls_after}"
|
||||
assert not any(ip_before in u for u in urls_after), (
|
||||
f"Stale URL with old IP {ip_before} still in registry: {urls_after}"
|
||||
)
|
||||
finally:
|
||||
_safe_force_delete(pod_name)
|
||||
|
||||
@@ -496,9 +498,11 @@ class TestGracefulDrain:
|
||||
# without affecting whether *this* pod's IP got removed. The
|
||||
# meaningful timing guarantee (`elapsed < grace_secs`) is below.
|
||||
_poll_until(
|
||||
lambda: not any(
|
||||
pod_ip in w["url"]
|
||||
for w in _get_workers(gateway_url).get("workers", [])
|
||||
lambda: (
|
||||
not any(
|
||||
pod_ip in w["url"]
|
||||
for w in _get_workers(gateway_url).get("workers", [])
|
||||
)
|
||||
),
|
||||
f"worker for ip {pod_ip} deregistered after graceful delete",
|
||||
timeout=RECONCILIATION_WAIT_SECS,
|
||||
|
||||
@@ -196,9 +196,9 @@ class TestMultiModelSelectorIsolation:
|
||||
# No URL should appear in both views — that would mean a
|
||||
# selector mismatch leaked a worker into the wrong gateway.
|
||||
cross_talk = set(llama_urls) & set(qwen_urls)
|
||||
assert (
|
||||
not cross_talk
|
||||
), f"Workers leaked across model selectors: {cross_talk}"
|
||||
assert not cross_talk, (
|
||||
f"Workers leaked across model selectors: {cross_talk}"
|
||||
)
|
||||
finally:
|
||||
for name in llama_workers + qwen_workers:
|
||||
_safe_force_delete(name)
|
||||
|
||||
@@ -181,9 +181,9 @@ class TestPDRolloutTypeChange:
|
||||
|
||||
by_type = _get_workers_by_type(pd_gateway)
|
||||
logger.info("Workers by type: %s", json.dumps(by_type, indent=2))
|
||||
assert (
|
||||
"prefill" in by_type
|
||||
), f"Expected prefill, got: {list(by_type.keys())}"
|
||||
assert "prefill" in by_type, (
|
||||
f"Expected prefill, got: {list(by_type.keys())}"
|
||||
)
|
||||
|
||||
finally:
|
||||
_safe_delete_pod(pod_name)
|
||||
@@ -257,12 +257,12 @@ class TestPDRolloutTypeChange:
|
||||
by_type = _get_workers_by_type(pd_gateway)
|
||||
logger.info("After rollout: %s", json.dumps(by_type, indent=2))
|
||||
|
||||
assert (
|
||||
"decode" in by_type
|
||||
), f"Expected decode worker after rollout, got: {list(by_type.keys())}"
|
||||
assert (
|
||||
"prefill" not in by_type
|
||||
), "Stale prefill worker persists after rollout"
|
||||
assert "decode" in by_type, (
|
||||
f"Expected decode worker after rollout, got: {list(by_type.keys())}"
|
||||
)
|
||||
assert "prefill" not in by_type, (
|
||||
"Stale prefill worker persists after rollout"
|
||||
)
|
||||
|
||||
finally:
|
||||
_safe_delete_pod(pod_name)
|
||||
|
||||
@@ -377,9 +377,9 @@ class TestReconciliationMetrics:
|
||||
{"source": "kubernetes", "result": "success"},
|
||||
)
|
||||
logger.info("Registration success metric: %s", reg_value)
|
||||
assert (
|
||||
reg_value is not None and reg_value >= 1
|
||||
), f"Expected at least 1 registration, got {reg_value}"
|
||||
assert reg_value is not None and reg_value >= 1, (
|
||||
f"Expected at least 1 registration, got {reg_value}"
|
||||
)
|
||||
|
||||
gauge_value = _parse_metric_value(
|
||||
metrics_text,
|
||||
@@ -387,9 +387,9 @@ class TestReconciliationMetrics:
|
||||
{"source": "kubernetes"},
|
||||
)
|
||||
logger.info("Workers discovered gauge: %s", gauge_value)
|
||||
assert (
|
||||
gauge_value is not None and gauge_value >= 1
|
||||
), f"Expected workers_discovered >= 1, got {gauge_value}"
|
||||
assert gauge_value is not None and gauge_value >= 1, (
|
||||
f"Expected workers_discovered >= 1, got {gauge_value}"
|
||||
)
|
||||
|
||||
finally:
|
||||
_safe_delete_worker_pod(pod_name)
|
||||
@@ -497,9 +497,9 @@ class TestReconciliationConsistency:
|
||||
|
||||
logger.info("Worker count samples over time: %s", samples)
|
||||
|
||||
assert all(
|
||||
s == stable_count for s in samples
|
||||
), f"Worker count fluctuated: {samples} (expected stable at {stable_count})"
|
||||
assert all(s == stable_count for s in samples), (
|
||||
f"Worker count fluctuated: {samples} (expected stable at {stable_count})"
|
||||
)
|
||||
|
||||
finally:
|
||||
for name in pod_names:
|
||||
|
||||
@@ -58,15 +58,15 @@ class TestStreamingEventsLocal:
|
||||
first_item_event = output_item_added_events[0]
|
||||
assert first_item_event.item is not None
|
||||
assert first_item_event.output_index is not None
|
||||
assert (
|
||||
first_item_event.output_index == 0
|
||||
), "First output item must have output_index: 0 (zero-based indexing)"
|
||||
assert first_item_event.output_index == 0, (
|
||||
"First output item must have output_index: 0 (zero-based indexing)"
|
||||
)
|
||||
|
||||
# Verify subsequent items increment correctly
|
||||
for i, event in enumerate(output_item_added_events):
|
||||
assert (
|
||||
event.output_index == i
|
||||
), f"Output item {i} should have output_index: {i}"
|
||||
assert event.output_index == i, (
|
||||
f"Output item {i} should have output_index: {i}"
|
||||
)
|
||||
|
||||
# Verify output_item.done event exists
|
||||
output_item_done_events = [
|
||||
@@ -101,9 +101,9 @@ class TestStreamingEventsLocal:
|
||||
output_item_added_events = [
|
||||
event for event in events if event.type == "response.output_item.added"
|
||||
]
|
||||
assert len(output_item_added_events) == len(
|
||||
output_array
|
||||
), "Number of output_item.added events should match output array length"
|
||||
assert len(output_item_added_events) == len(output_array), (
|
||||
"Number of output_item.added events should match output array length"
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -147,15 +147,15 @@ class TestStreamingEventsHarmony:
|
||||
first_item_event = output_item_added_events[0]
|
||||
assert first_item_event.item is not None
|
||||
assert first_item_event.output_index is not None
|
||||
assert (
|
||||
first_item_event.output_index == 0
|
||||
), "First output item must have output_index: 0 (zero-based indexing)"
|
||||
assert first_item_event.output_index == 0, (
|
||||
"First output item must have output_index: 0 (zero-based indexing)"
|
||||
)
|
||||
|
||||
# Verify subsequent items increment correctly
|
||||
for i, event in enumerate(output_item_added_events):
|
||||
assert (
|
||||
event.output_index == i
|
||||
), f"Output item {i} should have output_index: {i}"
|
||||
assert event.output_index == i, (
|
||||
f"Output item {i} should have output_index: {i}"
|
||||
)
|
||||
|
||||
# Verify output_item.done event exists
|
||||
output_item_done_events = [
|
||||
@@ -190,9 +190,9 @@ class TestStreamingEventsHarmony:
|
||||
output_item_added_events = [
|
||||
event for event in events if event.type == "response.output_item.added"
|
||||
]
|
||||
assert len(output_item_added_events) == len(
|
||||
output_array
|
||||
), "Number of output_item.added events should match output array length"
|
||||
assert len(output_item_added_events) == len(output_array), (
|
||||
"Number of output_item.added events should match output array length"
|
||||
)
|
||||
|
||||
def test_reasoning_content(self, setup_backend):
|
||||
"""Test that reasoning content has correct zero-based output_index.
|
||||
@@ -228,16 +228,16 @@ class TestStreamingEventsHarmony:
|
||||
# If reasoning is present, verify it has output_index: 0
|
||||
if reasoning_items:
|
||||
reasoning_item = reasoning_items[0]
|
||||
assert (
|
||||
reasoning_item.output_index == 0
|
||||
), "Reasoning item should have output_index: 0"
|
||||
assert reasoning_item.output_index == 0, (
|
||||
"Reasoning item should have output_index: 0"
|
||||
)
|
||||
|
||||
# If message is present after reasoning, verify it has output_index: 1
|
||||
if reasoning_items and message_items:
|
||||
message_item = message_items[0]
|
||||
assert (
|
||||
message_item.output_index == 1
|
||||
), "Message item after reasoning should have output_index: 1"
|
||||
assert message_item.output_index == 1, (
|
||||
"Message item after reasoning should have output_index: 1"
|
||||
)
|
||||
|
||||
# Find response.completed event
|
||||
completed_events = [
|
||||
|
||||
@@ -168,9 +168,9 @@ class TestToolCallingCloud:
|
||||
|
||||
# Check for function_call in output
|
||||
function_calls = [item for item in output if item.type == "function_call"]
|
||||
assert (
|
||||
len(function_calls) > 0
|
||||
), "Response should contain at least one function_call"
|
||||
assert len(function_calls) > 0, (
|
||||
"Response should contain at least one function_call"
|
||||
)
|
||||
|
||||
# Verify function_call structure
|
||||
function_call = function_calls[0]
|
||||
@@ -285,30 +285,30 @@ class TestToolCallingCloud:
|
||||
|
||||
event_types = [event.type for event in events]
|
||||
assert "response.created" in event_types, "Should have response.created event"
|
||||
assert (
|
||||
"response.completed" in event_types
|
||||
), "Should have response.completed event"
|
||||
assert (
|
||||
"response.output_item.added" in event_types
|
||||
), "Should have output_item.added events"
|
||||
assert (
|
||||
"response.mcp_list_tools.in_progress" in event_types
|
||||
), "Should have mcp_list_tools.in_progress event"
|
||||
assert (
|
||||
"response.mcp_list_tools.completed" in event_types
|
||||
), "Should have mcp_list_tools.completed event"
|
||||
assert (
|
||||
"response.mcp_call.in_progress" in event_types
|
||||
), "Should have mcp_call.in_progress event"
|
||||
assert (
|
||||
"response.mcp_call_arguments.delta" in event_types
|
||||
), "Should have mcp_call_arguments.delta event"
|
||||
assert (
|
||||
"response.mcp_call_arguments.done" in event_types
|
||||
), "Should have mcp_call_arguments.done event"
|
||||
assert (
|
||||
"response.mcp_call.completed" in event_types
|
||||
), "Should have mcp_call.completed event"
|
||||
assert "response.completed" in event_types, (
|
||||
"Should have response.completed event"
|
||||
)
|
||||
assert "response.output_item.added" in event_types, (
|
||||
"Should have output_item.added events"
|
||||
)
|
||||
assert "response.mcp_list_tools.in_progress" in event_types, (
|
||||
"Should have mcp_list_tools.in_progress event"
|
||||
)
|
||||
assert "response.mcp_list_tools.completed" in event_types, (
|
||||
"Should have mcp_list_tools.completed event"
|
||||
)
|
||||
assert "response.mcp_call.in_progress" in event_types, (
|
||||
"Should have mcp_call.in_progress event"
|
||||
)
|
||||
assert "response.mcp_call_arguments.delta" in event_types, (
|
||||
"Should have mcp_call_arguments.delta event"
|
||||
)
|
||||
assert "response.mcp_call_arguments.done" in event_types, (
|
||||
"Should have mcp_call_arguments.done event"
|
||||
)
|
||||
assert "response.mcp_call.completed" in event_types, (
|
||||
"Should have mcp_call.completed event"
|
||||
)
|
||||
|
||||
completed_events = [e for e in events if e.type == "response.completed"]
|
||||
assert len(completed_events) == 1
|
||||
@@ -336,18 +336,18 @@ class TestToolCallingCloud:
|
||||
assert mcp_call.output is not None
|
||||
|
||||
# Strict validation for cloud backends - check for text output events
|
||||
assert (
|
||||
"response.content_part.added" in event_types
|
||||
), "Should have content_part.added event"
|
||||
assert (
|
||||
"response.output_text.delta" in event_types
|
||||
), "Should have output_text.delta events"
|
||||
assert (
|
||||
"response.output_text.done" in event_types
|
||||
), "Should have output_text.done event"
|
||||
assert (
|
||||
"response.content_part.done" in event_types
|
||||
), "Should have content_part.done event"
|
||||
assert "response.content_part.added" in event_types, (
|
||||
"Should have content_part.added event"
|
||||
)
|
||||
assert "response.output_text.delta" in event_types, (
|
||||
"Should have output_text.delta events"
|
||||
)
|
||||
assert "response.output_text.done" in event_types, (
|
||||
"Should have output_text.done event"
|
||||
)
|
||||
assert "response.content_part.done" in event_types, (
|
||||
"Should have content_part.done event"
|
||||
)
|
||||
|
||||
assert "message" in final_output_types
|
||||
|
||||
@@ -400,9 +400,9 @@ class TestToolChoiceHarmony:
|
||||
assert len(output) > 0
|
||||
|
||||
function_calls = [item for item in output if item.type == "function_call"]
|
||||
assert (
|
||||
len(function_calls) > 0
|
||||
), "Model should choose to call function with tool_choice='auto'"
|
||||
assert len(function_calls) > 0, (
|
||||
"Model should choose to call function with tool_choice='auto'"
|
||||
)
|
||||
|
||||
def test_tool_choice_required(self, setup_backend):
|
||||
"""Test tool_choice="required" forces the model to call at least one tool."""
|
||||
@@ -423,9 +423,9 @@ class TestToolChoiceHarmony:
|
||||
|
||||
output = resp.output
|
||||
function_calls = [item for item in output if item.type == "function_call"]
|
||||
assert (
|
||||
len(function_calls) > 0
|
||||
), "tool_choice='required' must force at least one function call"
|
||||
assert len(function_calls) > 0, (
|
||||
"tool_choice='required' must force at least one function call"
|
||||
)
|
||||
|
||||
def test_tool_choice_specific_function(self, setup_backend):
|
||||
"""Test tool_choice with specific function name forces that function to be called."""
|
||||
@@ -447,9 +447,9 @@ class TestToolChoiceHarmony:
|
||||
output = resp.output
|
||||
function_calls = [item for item in output if item.type == "function_call"]
|
||||
assert len(function_calls) > 0, "Must call the specified function"
|
||||
assert (
|
||||
function_calls[0].name == "search_web"
|
||||
), "Must call the function specified in tool_choice"
|
||||
assert function_calls[0].name == "search_web", (
|
||||
"Must call the function specified in tool_choice"
|
||||
)
|
||||
|
||||
def test_tool_choice_streaming(self, setup_backend):
|
||||
"""Test tool_choice parameter works correctly with streaming."""
|
||||
|
||||
@@ -47,9 +47,9 @@ class TestMMLU:
|
||||
)
|
||||
metrics = run_eval(args)
|
||||
|
||||
assert (
|
||||
metrics["score"] >= 0.65
|
||||
), f"MMLU score {metrics['score']:.2f} below threshold 0.65"
|
||||
assert metrics["score"] >= 0.65, (
|
||||
f"MMLU score {metrics['score']:.2f} below threshold 0.65"
|
||||
)
|
||||
logger.info("MMLU score: %.2f (threshold: 0.65)", metrics["score"])
|
||||
|
||||
def test_mmlu_extended(self, setup_backend):
|
||||
@@ -70,7 +70,7 @@ class TestMMLU:
|
||||
)
|
||||
metrics = run_eval(args)
|
||||
|
||||
assert (
|
||||
metrics["score"] >= 0.65
|
||||
), f"MMLU score {metrics['score']:.2f} below threshold 0.65"
|
||||
assert metrics["score"] >= 0.65, (
|
||||
f"MMLU score {metrics['score']:.2f} below threshold 0.65"
|
||||
)
|
||||
logger.info("MMLU extended score: %.2f (threshold: 0.65)", metrics["score"])
|
||||
|
||||
@@ -55,7 +55,7 @@ class TestPDMMLU:
|
||||
)
|
||||
metrics = run_eval(args)
|
||||
|
||||
assert (
|
||||
metrics["score"] >= 0.65
|
||||
), f"PD MMLU score {metrics['score']:.2f} below threshold 0.65"
|
||||
assert metrics["score"] >= 0.65, (
|
||||
f"PD MMLU score {metrics['score']:.2f} below threshold 0.65"
|
||||
)
|
||||
logger.info("PD MMLU score: %.2f (threshold: 0.65)", metrics["score"])
|
||||
|
||||
@@ -201,9 +201,9 @@ class TestDisableHealthCheck:
|
||||
worker.metadata.get("disable_health_check"),
|
||||
)
|
||||
# Worker should be healthy immediately
|
||||
assert (
|
||||
worker.status == "healthy"
|
||||
), "Worker should be healthy when health checks disabled"
|
||||
assert worker.status == "healthy", (
|
||||
"Worker should be healthy when health checks disabled"
|
||||
)
|
||||
finally:
|
||||
gateway.shutdown()
|
||||
http_instance.release()
|
||||
|
||||
Reference in New Issue
Block a user