From 1995c0fcd02f88d5daa3fdbcd47e0f016e7b89b9 Mon Sep 17 00:00:00 2001 From: Kang Nahoon <49896157+ejrtks1020@users.noreply.github.com> Date: Thu, 8 Oct 2026 09:24:20 +0900 Subject: [PATCH] [Bugfix][Frontend] Apply output_config and thinking in Anthropic count_tokens (#59180) Signed-off-by: Kang Nahoon Co-authored-by: Flora Feng <4florafeng@gmail.com> --- .../test_anthropic_messages_conversion.py | 26 +++++++++++++++++++ vllm/entrypoints/anthropic/protocol.py | 2 ++ vllm/entrypoints/anthropic/serving.py | 8 +++--- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/tests/entrypoints/anthropic/test_anthropic_messages_conversion.py b/tests/entrypoints/anthropic/test_anthropic_messages_conversion.py index 1bccee4601..399c898d0b 100644 --- a/tests/entrypoints/anthropic/test_anthropic_messages_conversion.py +++ b/tests/entrypoints/anthropic/test_anthropic_messages_conversion.py @@ -1987,6 +1987,32 @@ class TestThinkingConfig: assert result.include_reasoning is True assert result.thinking_token_budget is None + @pytest.mark.parametrize( + "config", + [ + pytest.param({"output_config": {"effort": "low"}}, id="effort"), + pytest.param({"thinking": {"type": "disabled"}}, id="disabled"), + pytest.param( + {"thinking": {"type": "enabled", "budget_tokens": 2048}}, + id="enabled", + ), + ], + ) + def test_count_tokens_matches_messages(self, config): + """Effort and thinking change the rendered prompt, so count_tokens must + apply them the same way /v1/messages does.""" + messages = [{"role": "user", "content": "Hello"}] + expected = _convert( + AnthropicMessagesRequest( + model="test-model", max_tokens=4096, messages=messages, **config + ) + ) + result = _convert( + AnthropicCountTokensRequest(model="test-model", messages=messages, **config) + ) + assert result.reasoning_effort == expected.reasoning_effort + assert result.thinking_token_budget == expected.thinking_token_budget + class TestProbeDisabledThinkingEffort: """``auto`` falls back to ``low`` when ``none`` cannot turn thinking off.""" diff --git a/vllm/entrypoints/anthropic/protocol.py b/vllm/entrypoints/anthropic/protocol.py index 2a14c62933..5997d2e3d2 100644 --- a/vllm/entrypoints/anthropic/protocol.py +++ b/vllm/entrypoints/anthropic/protocol.py @@ -385,7 +385,9 @@ class AnthropicCountTokensRequest(BaseModel): model: str messages: list[AnthropicMessage] + output_config: AnthropicOutputConfig | None = None system: str | list[AnthropicContentBlock] | None = None + thinking: AnthropicThinkingConfig | None = None tool_choice: AnthropicToolChoice | None = None tools: list[AnthropicTool] | None = None diff --git a/vllm/entrypoints/anthropic/serving.py b/vllm/entrypoints/anthropic/serving.py index 05c1e24f2a..f11697fa16 100644 --- a/vllm/entrypoints/anthropic/serving.py +++ b/vllm/entrypoints/anthropic/serving.py @@ -633,8 +633,6 @@ class AnthropicServingMessages(OpenAIServingChat): ``display`` is intentionally ignored: suppressing reasoning would mark it ended for structured outputs and drop it from multi-turn history. """ - if isinstance(anthropic_request, AnthropicCountTokensRequest): - return thinking: AnthropicThinkingConfig | None = anthropic_request.thinking if thinking is None: return @@ -655,8 +653,6 @@ class AnthropicServingMessages(OpenAIServingChat): anthropic_request: AnthropicMessagesRequest | AnthropicCountTokensRequest, ) -> None: """Handle output configuration such as output format and effort.""" - if isinstance(anthropic_request, AnthropicCountTokensRequest): - return output_config: AnthropicOutputConfig | None = anthropic_request.output_config if output_config and output_config.format and output_config.format.json_schema: req.response_format = ResponseFormat( @@ -1238,9 +1234,13 @@ class AnthropicServingMessages(OpenAIServingChat): raw_request: Request | None = None, ) -> AnthropicCountTokensResponse | ErrorResponse: """Implements Anthropic's messages.count_tokens endpoint.""" + disabled_thinking_effort: AnthropicDisabledThinkingEffort = "none" + if request.thinking is not None and request.thinking.type == "disabled": + disabled_thinking_effort = await self._get_disabled_thinking_effort() chat_req = self.to_chat_completion_request( request, merge_inline_system=self._merge_inline_system, + disabled_thinking_effort=disabled_thinking_effort, ) result = await self.render_chat_request(chat_req) if isinstance(result, ErrorResponse):