Repository navigation
[Rust Frontend] Python parity run 2026-10-22 (e2e) - #30
richard-for-bugen[bot] wants to merge 8 commits into
Conversation
| # The Rust frontend rejects the null prompt while deserializing the body. | ||
| match=( | ||
| r"Either prompt or prompt_embeds must be provided and non-empty\." | ||
| r"|Failed to deserialize the JSON body.*prompt" | ||
| ), |
There was a problem hiding this comment.
I think Py's error reporting is better. Remove this entry from the PR and queue it to fix later
There was a problem hiding this comment.
Removed: the test_prompt_validation.py commit is dropped from this PR (rebased, now 7c0dae1), so the command stays commented out as on main and the test keeps Python's message unrelaxed. completions.prompt_missing_message stays queued as the fix: once Rust returns Python's "Either prompt or prompt_embeds must be provided and non-empty." for prompt: null, the test runs unchanged.
| # The Rust frontend needs a tokenizer.json, which facebook/opt-125m lacks. | ||
| MODEL_NAME = "HuggingFaceTB/SmolLM2-135M" |
There was a problem hiding this comment.
Yes, switched to Qwen/Qwen3-0.6B (folded into the same commit). On one GB200 GPU each (Slurm job 38739): Rust -k "not test_invalid_custom_logitsproc_arg" 2 passed, Python the whole file 4 passed. The target tokens 128 and 67 are ordinary byte-level tokens in Qwen3's vocabulary, not EOS, so the assertion keeps its meaning.
…Rust frontend The Rust frontend streams a leading chunk with only `prompt_token_ids` and a trailing chunk with only `finish_reason`, both with `logprobs: null`, which the OpenAI schema allows. The comparison test now skips chunks without logprobs; its token-id assertion is unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: richard-for-bugen[bot] <342439978+richard-for-bugen[bot]@users.noreply.github.com>
These tests pass under the Rust frontend on current main: - test_chat_completion: test_invalid_json_schema, test_invalid_regex - test_shutdown: the test_abort_timeout_exits_quickly clause matched nothing after the test was renamed - test_basic: server_load (15/15 across three servers) - test_serving_tokens: stream, test_generate_logprobs, stop_string_workflow - test_metrics: show, test_metrics_counts and the multimodal variants - tool_use: test_response_format_with_tool_choice_required, test_parallel_tool_calls_false, test_tool_call_and_choice - benchmarks/test_serve_cli: insecure and test_bench_serve The remaining exclusions now say why they exist. The OpenAI and Serve/Admin step timeouts grow by 10 minutes for the added tests. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: richard-for-bugen[bot] <342439978+richard-for-bugen[bot]@users.noreply.github.com>
…rom the Rust OpenAI step The three selected tests run against a MockEngine in-process and never reach the Rust frontend. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTSySQLs9bSuTKD9ScKhDb Signed-off-by: richard-for-bugen[bot] <342439978+richard-for-bugen[bot]@users.noreply.github.com>
…tend Add 11 commands from server-level test files that the Rust frontend already passes: chat echo, logprob token ids, thinking token budget, tensorizer, chunked prompt, optional middleware, multi API servers, routed experts (scale-out), function calling and the granite4 tool parsers. Four tests accept a Rust difference both sides allow: a string logprobs flag sent as a boolean, a finish-only last stream chunk and a role chunk without content, content null instead of empty, and an extra completion_tokens_details in usage. Raise the step timeouts to match. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTSySQLs9bSuTKD9ScKhDb Signed-off-by: richard-for-bugen[bot] <342439978+richard-for-bugen[bot]@users.noreply.github.com>
…s against the Rust frontend They pass: the Rust frontend ignores --api-server-count and serves them from one multithreaded process. Only the server_info cases stay excluded, because /server_info reports the Rust frontend's own config, which has no parallel_config. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTSySQLs9bSuTKD9ScKhDb Signed-off-by: richard-for-bugen[bot] <342439978+richard-for-bugen[bot]@users.noreply.github.com>
…Rust frontend The Rust frontend cannot load facebook/opt-125m, which ships no tokenizer.json. Move the test to Qwen/Qwen3-0.6B, updating only the prompt token counts, and run it in the Rust OpenAI Coverage step. Two relaxations where both behaviors are acceptable: the last streaming chunk may carry only the finish_reason (the joined text is still compared with the non-streaming output), and stream_options with a null option and stream=false may be accepted as unset. Excluded until Rust fixes land: test_single_completion (prompt_tokens_details with zero cached tokens), batched prompts (test_prompt_logprobs_completion, test_batch_completions), and n > 1 (test_parallel_*). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: richard-for-bugen[bot] <342439978+richard-for-bugen[bot]@users.noreply.github.com>
test_multiple_lora_adapters and test_beam_search_with_lora_adapters checked whether the done set of asyncio.wait held exceptions, but it holds tasks, so a failed load or completion never failed the test. Await the tasks with asyncio.gather instead. That exposed a real failure: the beam search test reused the adapter names of test_multiple_lora_adapters on the same module-scoped server, so its loads were rejected as already loaded. Give it its own names. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: richard-for-bugen[bot] <342439978+richard-for-bugen[bot]@users.noreply.github.com>
…nst the Rust frontend The Rust frontend cannot load facebook/opt-125m, which ships no tokenizer.json. Serve Qwen/Qwen3-0.6B in the online test instead, as test_completion.py does, and run it in the Rust OpenAI Coverage step. The offline tests keep opt-125m: they compare two LLM instances token by token, and with a bf16 SmolLM2-135M model that comparison flipped on a near-tied greedy token in two of three runs. test_invalid_custom_logitsproc_arg is excluded: the Rust frontend cannot run the processor's validate_params, so the invalid argument reaches the engine instead of returning 400. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: richard-for-bugen[bot] <342439978+richard-for-bugen[bot]@users.noreply.github.com>
0f75884 to
7c0dae1
Compare
|
Merged upstream as vllm-project#63320. |
Summary
First
e2erun PR: step 1 of the backfill that pins Rust parity with Python's own server-level tests. Based on upstreammainataa31fa038f. It goes through every-kexclusion and commented-out command in.buildkite/test_areas/rust_frontend.yaml, runs each one locally against the Rust frontend (and against Python for every failure), and enables the ones that pass. 10 exclusions are dropped (one of them with a test relaxation); the remaining ones now say why they exist. Step 2 adds 11 commands from server-level test files that were not in the Rust CI yet (four with a relaxation).Only
tests/and.buildkite/test_areas/rust_frontend.yamlchange. A test is relaxed only where both behaviors are semantically acceptable; no assertion on the behavior under test is weakened.Items
test_chat_completion.py::test_invalid_json_schema,::test_invalid_regextest_return_token_ids.py::test_comparison_with_prompt_logprobs_and_logprobsprompt_token_idschunk and a trailingfinish_reasonchunk withlogprobs: null(nullable in the OpenAI schema); the test dereferencedlogprobs.tokenson every chunktest_shutdown.pytest_abort_timeout_exits_quicklywas renamed (test_abort_timeout_exits_within_cleanup_grace), so the clause matched nothingbenchmarks/test_serve_cli.pyinsecure,test_bench_servetest_basic.pyserver_loadtest_serving_tokens.pystream,test_generate_logprobs,stop_string_workflowtop_logprobstruncation comment was stale. Fourstreamcases self-skip under Rust-k "not lora"test_metrics.pyshow,test_metrics_counts, multimodal variantstest_metrics_countsself-skips)-k "not run_batch and not test_metrics_exist"test_serving_chat.pythree-k-selected testsMockEnginein-process and never reach Rustv1/distributed/test_{internal,external,hybrid}_lb_dp.py4cases (--api-server-count 4)-k "not server_info"test_response_format_with_tool_choice_required,test_parallel_tool_calls_false,test_tool_call_and_choiceunsloth/Llama-3.2-3B-Instructwith the same config passes 5/5 eachStep 2, new commands (each passes locally under Rust with
--gpu-memory-utilizationcapped to the CI slice size):chat_completion/test_chat_echo.py"logprobs": "true"(a string); the OpenAI schema says boolean, and Rust only accepts a boolean, so it sendsTruechat_completion/test_logprob_token_ids.pychat_completion/test_thinking_token_budget.py -k "not qwen35"completion/test_tensorizer_entrypoint.pycompletion_tokens_details, so the usage check compares the three counts instead of the whole objecttest_chunked_prompt.pycontent; every chunk that carries tokens is still checkedserve/middleware/test_optional_middleware.py -k "not test_v2_endpoint_accepts_valid_api_token"/v2/embed(pooling)launchers/api_server/test_multi_api_servers.py--api-server-countand runs one server, so this only checks the flag is acceptedrl/return_routed_experts/test_scale_out_api.pychat_completion/test_completion_with_function_calling.py -k "not test_no_args_tool_call"content: nullin Rust and""in Python. The empty-argumentshistory case is a Rust bug (500); the{}case passes but shares the nametool_parsers/test_granite4_tool_parser.pytool_parsers/test_hermes_tool_parser.py -k granite4--lora-modulesTests that need a pending Rust fix (the LoRA request field, the gpt-oss parser selection, a single structured-outputs backend, ORCA headers, and others) are tracked in the parity ledger and land in a later run.
Still excluded, each with a comment (Rust fixes tracked in the parity ledger):
test_kv_transfer_prompt_token_ids_*: Rust re-rendersmessagesinstead of reusingkv_transfer_params["prompt_token_ids"].completion/test_prompt_validation.py(stays commented out, per review):test_empty_promptgets a body-deserialization 400 forprompt: nullinstead of Python's "Either prompt or prompt_embeds must be provided and non-empty." The Rust fix (completions.prompt_missing_message) comes first; the test then runs unrelaxed.test_chat_logit_bias_multiple_non_integer_keys: enabled together with its fix in [Rust Frontend] Report every invalid logit_bias key like Python vllm-project/vllm#63313 ([Rust Frontend] Report every invalid logit_bias key like Python #32); this PR leaves that line alone.test_serving_tokens.pylora: the RustLoraRequeststill encodestensorizer_config_dict, removed by [LoRA] Remove tensorizer vllm-project/vllm#60024, so--lora-modulesfails at startup; the fix is [Bugfix][Rust Frontend] LoraRequest: drop tensorizer_config_dict to match the Python wire layout vllm-project/vllm#63295.test_metrics.pytest_metrics_exist:vllm:admission_rejections(open [Rust Frontend] Support--max-num-queued-reqsand--max-num-queued-tokensvllm-project/vllm#61296) and image inputs.test_tokenization.pytokenizer_info: Rust returns 501 (the old comment said no-op).tool_use/mistral: nomistraltokenizer mode or renderer in Rust.test_shutdown_on_engine_failure: launches the Python API server entry directly.test_*_lb_dp.pyserver_info:/server_inforeports the Rust frontend's own config, which has noparallel_config; theapi-server-count=4variants also expect four API processes.Step timeouts, NVIDIA (AMD): OpenAI Coverage 30→50 (35→55), Serve/Admin 25→45 (30→50), Tool Use 25→35 (30→40). Locally, step 1 added about 10 minutes each to OpenAI Coverage and Serve/Admin (
test_metrics.pyalone took 6 andtest_basic.pywithserver_load4), and step 2 adds about 10, 7 and 9 minutes to the three steps.Validation
Local, GB200,
VLLM_USE_RUST_FRONTEND=1,vllm-rsbuilt from this base withVLLM_RS_BUILD_VERSIONset to the vLLM version;--gpu-memory-utilizationcapped through a pytest plugin (another job shares the GPUs), no test-file change. Each changed command, exactly as in the YAML:Step 2, under Rust, with memory capped to an 18 GB slice (35 GB for Tool Use):
Every relaxed test also passes under the Python frontend.
Distributed step, on a separate clean 4-GPU node, the previously excluded cases only (
-k "4 or server_info"):