fix(vertex): stop stripping output_config and output_format from VertexAI Claude requests - #23475
fix(vertex): stop stripping output_config and output_format from VertexAI Claude requests#23475netbrah wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR fixes a silent parameter-stripping bug in the VertexAI Claude integration: two Key changes:
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py | Removes .pop("output_config") and .pop("output_format") stripping from transform_request; the map_openai_params override still forces tool-based structured outputs for the OpenAI-compat path, so the OpenAI → Vertex flow is unaffected. |
| litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/experimental_pass_through/transformation.py | Removes the same two .pop() calls from the Messages API pass-through path; only the model field is still stripped, which is correct per Vertex AI's API contract. |
| tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/test_vertex_ai_partner_models_anthropic_transformation.py | Tests correctly flipped from "verify dropped" to "verify preserved"; one assertion in test_vertex_ai_anthropic_output_format_and_output_config_both_preserved becomes trivially true because model is no longer injected into test_data before the mock, mildly weakening coverage of the model-stripping behaviour. |
Sequence Diagram
sequenceDiagram
participant Caller
participant VertexAIAnthropicConfig
participant AnthropicConfig (parent)
participant VertexAI
Note over Caller,VertexAI: OpenAI-compat path (response_format)
Caller->>VertexAIAnthropicConfig: map_openai_params(response_format=...)
VertexAIAnthropicConfig->>AnthropicConfig (parent): map_openai_params(model="claude-3-sonnet-20240229")
AnthropicConfig (parent)-->>VertexAIAnthropicConfig: optional_params with tools+tool_choice (tool-based, no output_format)
VertexAIAnthropicConfig-->>Caller: optional_params (tools, no output_format)
Caller->>VertexAIAnthropicConfig: transform_request(optional_params)
VertexAIAnthropicConfig->>AnthropicConfig (parent): super().transform_request()
AnthropicConfig (parent)-->>VertexAIAnthropicConfig: data (no output_format since not in optional_params)
VertexAIAnthropicConfig->>VertexAIAnthropicConfig: data.pop("model")
VertexAIAnthropicConfig-->>VertexAI: request body (tools-based, no output_format)
Note over Caller,VertexAI: Native Anthropic API path (pass-through)
Caller->>VertexAIAnthropicConfig: transform_request(optional_params with output_config/output_format)
VertexAIAnthropicConfig->>AnthropicConfig (parent): super().transform_request()
AnthropicConfig (parent)-->>VertexAIAnthropicConfig: data (includes output_config + output_format)
VertexAIAnthropicConfig->>VertexAIAnthropicConfig: data.pop("model")
Note over VertexAIAnthropicConfig: output_config/output_format NO LONGER stripped (this PR)
VertexAIAnthropicConfig-->>VertexAI: request body (includes output_config + output_format)
Last reviewed commit: "fix(vertex): stop st..."
f06e8d9 to
703145a
Compare
…exAI Claude requests VertexAI Claude now supports output_config (effort control) and output_format (structured JSON outputs) as of early 2026. The previous behavior silently dropped these parameters, causing structured output requests to return unstructured text. Removes the .pop() calls in both the Messages API path (experimental_pass_through/transformation.py) and the Chat Completions path (transformation.py). Updates tests to verify the parameters are preserved. Fixes BerriAI#23380 Made-with: Cursor
703145a to
93d04b2
Compare
…ept it Resolves the silent strip of Anthropic Structured Outputs across the Vertex AI Claude transformation paths and the Anthropic-adapter re-merge. Consolidates and supersedes four stalled community PRs addressing overlapping aspects of the same root bug: - #23475 (Vertex AI Claude blanket-strip removal) - #23396 (Vertex AI Claude conditional passthrough) - #23706 (Anthropic adapter exclude output_config from non-Anthropic backends) - #22727 (Anthropic adapter strip output_config for non-Anthropic backends) Closes / addresses: #23380 (Vertex AI Claude output_config drop), related: #26423, #25079, #24549, #25971, #25957, #26163, #24856. What was broken --------------- * Vertex AI Claude paths called ``data.pop("output_config")`` and ``data.pop("output_format")`` unconditionally even when Vertex accepted those fields. Callers asking for Structured Outputs got a 200 with prose and never knew the schema constraints had been silently dropped (often masked for months by permissive fallback parsers). * The ``/v1/messages`` -> ``/chat/completions`` adapter (``LiteLLMMessagesToCompletionTransformationHandler``) re-merged the raw Anthropic-shaped ``output_config`` into ``completion_kwargs`` AFTER the translator already mapped its meaningful parts to ``response_format`` / ``reasoning_effort``. Non-Anthropic backends (Azure OpenAI, Fireworks, Bedrock Nova, etc.) then 400'd with "Extra inputs are not permitted". Approach -------- Vertex AI Claude (chat-completion + experimental_pass_through paths): Replace the unconditional pop with a sanitizer ``_sanitize_vertex_anthropic_output_params`` that strips only the Vertex-unsupported keys (today: ``effort``) from ``output_config`` while forwarding ``format`` and the legacy top-level ``output_format``. Defensive: non-dict ``output_config`` values are dropped to avoid sending malformed payloads downstream. Greptile P1 from PR #23396 addressed: when ``output_config`` carries both ``format`` and ``effort``, the prior conditional pass-through forwarded ``effort`` and reproduced the 400. The new helper filters per-key. Anthropic ``/v1/messages`` adapter: Add ``output_config`` to a named module-level constant ``ANTHROPIC_ONLY_REQUEST_KEYS`` and wire it into ``excluded_keys`` so the post-translation re-merge skips re-adding the raw key. This fixes the 400 on non-Anthropic backends and avoids the conflicting duplicate (``response_format`` + raw ``output_config``) on Anthropic-family backends. Greptile P2 from PR #23706 addressed: the constant gives reviewers one grep target instead of an inline literal that silently grows. Greptile P2 from PR #22727 addressed: ``extra_kwargs or {}`` is replaced with explicit ``is None`` checks so empty-dict callers no longer skip the fallback path. Tests ----- * tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/ test_vertex_ai_partner_models_anthropic_transformation.py: - 5 new/updated cases plus a direct unit test for ``_sanitize_vertex_anthropic_output_params``. - Updated ``test_vertex_ai_claude_sonnet_4_5_structured_output_fix`` so its mock-injected ``output_format`` is asserted to FLOW THROUGH (the original test asserted the now-buggy strip behavior). * tests/test_litellm/llms/anthropic/experimental_pass_through/ adapters/test_handler_output_config_passthrough.py (new): - Constant export sanity, output_config strip with ``effort`` only, output_config strip with ``format`` only, regression guard that unrelated extras still flow, explicit-empty-dict path, and the ``extra_kwargs=None`` no-crash path. Test-quality fixes incorporated from Greptile review on the superseded PRs: * No ``inspect.getsource`` source-text assertions (PR #24114 / #23475). * ``sys.path`` insertion is anchored to ``__file__`` (PR #23706). * Assertion messages are positional, not tuple (PR #24114-class bug). * No ``or {}`` masking explicit empty dicts in helper signatures (PR #22727). Verified locally: 26/26 pass with this commit. The new tests fail (or fail to import) on ``main`` without it. Out of scope ------------ * The ``max_tokens`` capping logic from PR #22727 — independent concern, deserves its own PR with a focused test plan. * Architectural rework of the ``excluded_keys`` mechanism (Greptile P2 on PR #23706 noted point-fix growth). The named constant gives maintainers a clear place to extend; a registry-based approach would be a follow-up. Co-Authored-By: netbrah <netbrah> Co-Authored-By: s-zx <s-zx> Co-Authored-By: invoicepulse <invoicepulse> Co-Authored-By: cfdude <cfdude> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ept it Resolves the silent strip of Anthropic Structured Outputs across the Vertex AI Claude transformation paths and the Anthropic-adapter re-merge. Consolidates and supersedes four stalled community PRs addressing overlapping aspects of the same root bug: - BerriAI#23475 (Vertex AI Claude blanket-strip removal) - BerriAI#23396 (Vertex AI Claude conditional passthrough) - BerriAI#23706 (Anthropic adapter exclude output_config from non-Anthropic backends) - BerriAI#22727 (Anthropic adapter strip output_config for non-Anthropic backends) Closes / addresses: BerriAI#23380 (Vertex AI Claude output_config drop), related: BerriAI#26423, BerriAI#25079, BerriAI#24549, BerriAI#25971, BerriAI#25957, BerriAI#26163, BerriAI#24856. What was broken --------------- * Vertex AI Claude paths called ``data.pop("output_config")`` and ``data.pop("output_format")`` unconditionally even when Vertex accepted those fields. Callers asking for Structured Outputs got a 200 with prose and never knew the schema constraints had been silently dropped (often masked for months by permissive fallback parsers). * The ``/v1/messages`` -> ``/chat/completions`` adapter (``LiteLLMMessagesToCompletionTransformationHandler``) re-merged the raw Anthropic-shaped ``output_config`` into ``completion_kwargs`` AFTER the translator already mapped its meaningful parts to ``response_format`` / ``reasoning_effort``. Non-Anthropic backends (Azure OpenAI, Fireworks, Bedrock Nova, etc.) then 400'd with "Extra inputs are not permitted". Approach -------- Vertex AI Claude (chat-completion + experimental_pass_through paths): Replace the unconditional pop with a sanitizer ``_sanitize_vertex_anthropic_output_params`` that strips only the Vertex-unsupported keys (today: ``effort``) from ``output_config`` while forwarding ``format`` and the legacy top-level ``output_format``. Defensive: non-dict ``output_config`` values are dropped to avoid sending malformed payloads downstream. Greptile P1 from PR BerriAI#23396 addressed: when ``output_config`` carries both ``format`` and ``effort``, the prior conditional pass-through forwarded ``effort`` and reproduced the 400. The new helper filters per-key. Anthropic ``/v1/messages`` adapter: Add ``output_config`` to a named module-level constant ``ANTHROPIC_ONLY_REQUEST_KEYS`` and wire it into ``excluded_keys`` so the post-translation re-merge skips re-adding the raw key. This fixes the 400 on non-Anthropic backends and avoids the conflicting duplicate (``response_format`` + raw ``output_config``) on Anthropic-family backends. Greptile P2 from PR BerriAI#23706 addressed: the constant gives reviewers one grep target instead of an inline literal that silently grows. Greptile P2 from PR BerriAI#22727 addressed: ``extra_kwargs or {}`` is replaced with explicit ``is None`` checks so empty-dict callers no longer skip the fallback path. Tests ----- * tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/ test_vertex_ai_partner_models_anthropic_transformation.py: - 5 new/updated cases plus a direct unit test for ``_sanitize_vertex_anthropic_output_params``. - Updated ``test_vertex_ai_claude_sonnet_4_5_structured_output_fix`` so its mock-injected ``output_format`` is asserted to FLOW THROUGH (the original test asserted the now-buggy strip behavior). * tests/test_litellm/llms/anthropic/experimental_pass_through/ adapters/test_handler_output_config_passthrough.py (new): - Constant export sanity, output_config strip with ``effort`` only, output_config strip with ``format`` only, regression guard that unrelated extras still flow, explicit-empty-dict path, and the ``extra_kwargs=None`` no-crash path. Test-quality fixes incorporated from Greptile review on the superseded PRs: * No ``inspect.getsource`` source-text assertions (PR BerriAI#24114 / BerriAI#23475). * ``sys.path`` insertion is anchored to ``__file__`` (PR BerriAI#23706). * Assertion messages are positional, not tuple (PR BerriAI#24114-class bug). * No ``or {}`` masking explicit empty dicts in helper signatures (PR BerriAI#22727). Verified locally: 26/26 pass with this commit. The new tests fail (or fail to import) on ``main`` without it. Out of scope ------------ * The ``max_tokens`` capping logic from PR BerriAI#22727 — independent concern, deserves its own PR with a focused test plan. * Architectural rework of the ``excluded_keys`` mechanism (Greptile P2 on PR BerriAI#23706 noted point-fix growth). The named constant gives maintainers a clear place to extend; a registry-based approach would be a follow-up. Co-Authored-By: netbrah <netbrah> Co-Authored-By: s-zx <s-zx> Co-Authored-By: invoicepulse <invoicepulse> Co-Authored-By: cfdude <cfdude> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
…ept it Resolves the silent strip of Anthropic Structured Outputs across the Vertex AI Claude transformation paths and the Anthropic-adapter re-merge. Consolidates and supersedes four stalled community PRs addressing overlapping aspects of the same root bug: - BerriAI#23475 (Vertex AI Claude blanket-strip removal) - BerriAI#23396 (Vertex AI Claude conditional passthrough) - BerriAI#23706 (Anthropic adapter exclude output_config from non-Anthropic backends) - BerriAI#22727 (Anthropic adapter strip output_config for non-Anthropic backends) Closes / addresses: BerriAI#23380 (Vertex AI Claude output_config drop), related: BerriAI#26423, BerriAI#25079, BerriAI#24549, BerriAI#25971, BerriAI#25957, BerriAI#26163, BerriAI#24856. What was broken --------------- * Vertex AI Claude paths called ``data.pop("output_config")`` and ``data.pop("output_format")`` unconditionally even when Vertex accepted those fields. Callers asking for Structured Outputs got a 200 with prose and never knew the schema constraints had been silently dropped (often masked for months by permissive fallback parsers). * The ``/v1/messages`` -> ``/chat/completions`` adapter (``LiteLLMMessagesToCompletionTransformationHandler``) re-merged the raw Anthropic-shaped ``output_config`` into ``completion_kwargs`` AFTER the translator already mapped its meaningful parts to ``response_format`` / ``reasoning_effort``. Non-Anthropic backends (Azure OpenAI, Fireworks, Bedrock Nova, etc.) then 400'd with "Extra inputs are not permitted". Approach -------- Vertex AI Claude (chat-completion + experimental_pass_through paths): Replace the unconditional pop with a sanitizer ``_sanitize_vertex_anthropic_output_params`` that strips only the Vertex-unsupported keys (today: ``effort``) from ``output_config`` while forwarding ``format`` and the legacy top-level ``output_format``. Defensive: non-dict ``output_config`` values are dropped to avoid sending malformed payloads downstream. Greptile P1 from PR BerriAI#23396 addressed: when ``output_config`` carries both ``format`` and ``effort``, the prior conditional pass-through forwarded ``effort`` and reproduced the 400. The new helper filters per-key. Anthropic ``/v1/messages`` adapter: Add ``output_config`` to a named module-level constant ``ANTHROPIC_ONLY_REQUEST_KEYS`` and wire it into ``excluded_keys`` so the post-translation re-merge skips re-adding the raw key. This fixes the 400 on non-Anthropic backends and avoids the conflicting duplicate (``response_format`` + raw ``output_config``) on Anthropic-family backends. Greptile P2 from PR BerriAI#23706 addressed: the constant gives reviewers one grep target instead of an inline literal that silently grows. Greptile P2 from PR BerriAI#22727 addressed: ``extra_kwargs or {}`` is replaced with explicit ``is None`` checks so empty-dict callers no longer skip the fallback path. Tests ----- * tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/ test_vertex_ai_partner_models_anthropic_transformation.py: - 5 new/updated cases plus a direct unit test for ``_sanitize_vertex_anthropic_output_params``. - Updated ``test_vertex_ai_claude_sonnet_4_5_structured_output_fix`` so its mock-injected ``output_format`` is asserted to FLOW THROUGH (the original test asserted the now-buggy strip behavior). * tests/test_litellm/llms/anthropic/experimental_pass_through/ adapters/test_handler_output_config_passthrough.py (new): - Constant export sanity, output_config strip with ``effort`` only, output_config strip with ``format`` only, regression guard that unrelated extras still flow, explicit-empty-dict path, and the ``extra_kwargs=None`` no-crash path. Test-quality fixes incorporated from Greptile review on the superseded PRs: * No ``inspect.getsource`` source-text assertions (PR BerriAI#24114 / BerriAI#23475). * ``sys.path`` insertion is anchored to ``__file__`` (PR BerriAI#23706). * Assertion messages are positional, not tuple (PR BerriAI#24114-class bug). * No ``or {}`` masking explicit empty dicts in helper signatures (PR BerriAI#22727). Verified locally: 26/26 pass with this commit. The new tests fail (or fail to import) on ``main`` without it. Out of scope ------------ * The ``max_tokens`` capping logic from PR BerriAI#22727 — independent concern, deserves its own PR with a focused test plan. * Architectural rework of the ``excluded_keys`` mechanism (Greptile P2 on PR BerriAI#23706 noted point-fix growth). The named constant gives maintainers a clear place to extend; a registry-based approach would be a follow-up. Co-Authored-By: netbrah <netbrah> Co-Authored-By: s-zx <s-zx> Co-Authored-By: invoicepulse <invoicepulse> Co-Authored-By: cfdude <cfdude>
…sthrough-consolidated fix(adapters,vertex): pass output_config through to backends that accept it (closes BerriAI#23380, supersedes BerriAI#23475/BerriAI#23396/BerriAI#23706/BerriAI#22727)
Summary
Fixes #23380
VertexAI Claude now supports
output_config(effort control) andoutput_format(structured JSON outputs) — these parameters were being silently stripped, causing structured output requests to return unstructured text andeffortconstraints to be ignored.Root cause: Two explicit
.pop()calls in the VertexAI Anthropic transformation code that were added when Vertex didn't support these params. Vertex has since added support (confirmed via Anthropic docs and Google Cloud docs).Fix: Remove the
.pop("output_config")and.pop("output_format")calls in both:experimental_pass_through/transformation.py)transformation.py)Changes
litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/experimental_pass_through/transformation.py— remove output_config and output_format strippinglitellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py— sameTest plan
test_vertex_ai_anthropic_output_config_preserved— verifies output_config with effort passes throughtest_vertex_ai_anthropic_output_format_and_output_config_both_preserved— verifies both params pass throughtest_vertex_ai_claude_sonnet_4_5_structured_output_fix— existing test still passestest_vertex_ai_anthropic_structured_output_header_not_added— existing test still passesMade with Cursor