Skip to content

Fix: Vertex AI Gemini labels field provider-aware filtering - #14563

Merged
ishaan-jaff merged 4 commits into
BerriAI:mainfrom
timelfrink:fix/issue-14556-vertex-ai-labels-provider-filtering
Sep 15, 2025
Merged

Fix: Vertex AI Gemini labels field provider-aware filtering#14563
ishaan-jaff merged 4 commits into
BerriAI:mainfrom
timelfrink:fix/issue-14556-vertex-ai-labels-provider-filtering

Conversation

@timelfrink

@timelfrink timelfrink commented Sep 15, 2025

Copy link
Copy Markdown
Contributor

Title

Fix: Vertex AI Gemini labels field provider-aware filtering

Relevant issues

Fixes #14556

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • I have added a screenshot of my new test passing locally
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem

Type

🐛 Bug Fix

Changes

What this PR does

This PR fixes a critical issue where Google GenAI endpoints (generativelanguage.googleapis.com) failed when receiving requests with the labels field, which is only supported by Vertex AI endpoints (aiplatform.googleapis.com). The fix implements provider-aware filtering to include labels only for Vertex AI endpoints while excluding them for Google GenAI endpoints.

Problem being solved

Issue #14556 reported that when using LiteLLM with Google GenAI endpoints, requests would fail because the labels field was being included in the request payload. Google GenAI's API doesn't support the labels field (which is a Vertex AI specific feature), causing API errors and request failures.

Core Implementation Changes

  • litellm/llms/vertex_ai/gemini/transformation.py:408-420: Added _is_google_genai_endpoint() helper function to detect Google GenAI vs Vertex AI endpoints based on the API base URL
  • litellm/llms/vertex_ai/gemini/transformation.py:427: Updated _transform_request_body() to accept api_base parameter for provider detection
  • litellm/llms/vertex_ai/gemini/transformation.py:508-509: Modified labels inclusion logic to only add labels for Vertex AI endpoints (not Google GenAI)
  • litellm/llms/vertex_ai/gemini/transformation.py:561,609: Updated sync and async transform functions to pass api_base parameter through the transformation chain

Testing Changes

Added comprehensive test coverage in tests/test_litellm/llms/vertex_ai/gemini/test_vertex_ai_gemini_transformation.py:

  • test_google_genai_excludes_labels(): Verifies Google GenAI endpoints exclude labels even when explicitly provided
  • test_vertex_ai_includes_labels(): Confirms Vertex AI endpoints include labels when provided
  • test_provider_detection(): Tests the endpoint detection logic for various URL patterns
  • test_metadata_to_labels_vertex_only(): Ensures metadata-to-labels conversion only happens for Vertex AI

How to verify it

Automated Verification

  • Tests pass: poetry run pytest tests/test_litellm/llms/vertex_ai/gemini/test_vertex_ai_gemini_transformation.py -v
  • Linting passes: ruff check litellm/llms/vertex_ai/gemini/transformation.py
  • Type checking passes: mypy litellm/llms/vertex_ai/gemini/transformation.py
  • Integration tests pass: poetry run pytest tests/test_litellm/test_completion.py -k vertex_ai -v

Manual Verification

  • Google GenAI requests with labels succeed (labels are filtered out)
  • Vertex AI requests with labels continue to work (labels are included)
  • No regression in existing Vertex AI functionality
  • Endpoint detection correctly identifies provider type

Breaking changes

None - this is a backward-compatible bug fix that maintains existing functionality while fixing the Google GenAI compatibility issue.

Migration required

None - existing code will continue to work without changes.

Related issues/PRs

- Test Google GenAI endpoints exclude labels even when explicitly provided
- Test Vertex AI endpoints include labels when provided
- Cover provider detection logic for different endpoint URLs
- Verify metadata-to-labels conversion only happens for Vertex AI
- Ensure edge cases are handled properly (null/empty api_base)
- Add _is_google_genai_endpoint() function to detect Google GenAI vs Vertex AI endpoints
- Update _transform_request_body() to accept api_base parameter
- Only include labels field for Vertex AI endpoints (not Google GenAI)
- Pass api_base through sync/async transform functions
- Maintain backward compatibility with existing usage
- Fixes issue where Google GenAI requests failed with unsupported labels field
@vercel

vercel Bot commented Sep 15, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
litellm Ready Ready Preview Comment Sep 15, 2025 7:21pm

@ahmadmucom

Copy link
Copy Markdown

This is really critical, any temp workaround for it ?

@ishaan-jaff ishaan-jaff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread litellm/llms/vertex_ai/gemini/transformation.py Outdated
…arsing

Replace URL-based endpoint detection with custom_llm_provider parameter
checking for cleaner, more reliable provider identification.

Changes:
- Remove _is_google_genai_endpoint() helper function
- Update labels condition to use custom_llm_provider != "gemini"
- Remove api_base parameter from _transform_request_body()
- Simplify sync/async transform function signatures
- Update tests to reflect new parameter structure
- Remove obsolete test_provider_detection test

This approach aligns with existing codebase patterns where
custom_llm_provider="gemini" identifies Google AI Studio endpoints
that don't support labels, while vertex_ai/vertex_ai_beta identify
Vertex AI endpoints that do support labels.
@noahkiss

Copy link
Copy Markdown

Might now be a good time to include updated error messages, so that VertexAIException is not the hard-coded error for Gemini API calls (which confused many of us who were initially trying to figure out what broke over the past 24 hr)?

elif (
custom_llm_provider == "vertex_ai"
or custom_llm_provider == "vertex_ai_beta"
or custom_llm_provider == "gemini"
):
if (
"Vertex AI API has not been used in project" in error_str
or "Unable to find your project" in error_str
):
exception_mapping_worked = True
raise BadRequestError(
message=f"litellm.BadRequestError: VertexAIException - {error_str}",
model=model,
llm_provider="vertex_ai",
response=httpx.Response(
status_code=400,
request=httpx.Request(
method="POST",
url=" https://cloud.google.com/vertex-ai/",
),
),
litellm_debug_info=extra_information,
)
if "400 Request payload size exceeds" in error_str:
exception_mapping_worked = True
raise ContextWindowExceededError(
message=f"VertexException - {error_str}",
model=model,
llm_provider=custom_llm_provider,
)
elif (
"None Unknown Error." in error_str
or "Content has no parts." in error_str
):
exception_mapping_worked = True
raise litellm.InternalServerError(
message=f"litellm.InternalServerError: VertexAIException - {error_str}",
model=model,
llm_provider="vertex_ai",
response=httpx.Response(
status_code=500,
content=str(original_exception),
request=httpx.Request(method="completion", url="https://github.com/BerriAI/litellm"), # type: ignore
),
litellm_debug_info=extra_information,
)
elif "API key not valid." in error_str:
exception_mapping_worked = True
raise AuthenticationError(
message=f"{custom_llm_provider}Exception - {error_str}",
model=model,
llm_provider=custom_llm_provider,
litellm_debug_info=extra_information,
)
elif "403" in error_str:
exception_mapping_worked = True
raise BadRequestError(
message=f"VertexAIException BadRequestError - {error_str}",
model=model,
llm_provider="vertex_ai",
response=httpx.Response(
status_code=403,
request=httpx.Request(
method="POST",
url=" https://cloud.google.com/vertex-ai/",
),
),
litellm_debug_info=extra_information,
)
elif (
"The response was blocked." in error_str
or "Output blocked by content filtering policy"
in error_str # anthropic on vertex ai
):
exception_mapping_worked = True
raise ContentPolicyViolationError(
message=f"VertexAIException ContentPolicyViolationError - {error_str}",
model=model,
llm_provider="vertex_ai",
litellm_debug_info=extra_information,
response=httpx.Response(
status_code=400,
request=httpx.Request(
method="POST",
url=" https://cloud.google.com/vertex-ai/",
),
),
)
elif (
"429 Quota exceeded" in error_str
or "Quota exceeded for" in error_str
or "IndexError: list index out of range" in error_str
or "429 Unable to submit request because the service is temporarily out of capacity."
in error_str
):
exception_mapping_worked = True
raise RateLimitError(
message=f"litellm.RateLimitError: VertexAIException - {error_str}",
model=model,
llm_provider="vertex_ai",
litellm_debug_info=extra_information,
response=httpx.Response(
status_code=429,
request=httpx.Request(
method="POST",
url=" https://cloud.google.com/vertex-ai/",
),
),
)
elif (
"500 Internal Server Error" in error_str
or "The model is overloaded." in error_str
):
exception_mapping_worked = True
raise litellm.InternalServerError(
message=f"litellm.InternalServerError: VertexAIException - {error_str}",
model=model,
llm_provider="vertex_ai",
litellm_debug_info=extra_information,
)
if hasattr(original_exception, "status_code"):
if original_exception.status_code == 400:
exception_mapping_worked = True
raise BadRequestError(
message=f"VertexAIException BadRequestError - {error_str}",
model=model,
llm_provider="vertex_ai",
litellm_debug_info=extra_information,
response=httpx.Response(
status_code=400,
request=httpx.Request(
method="POST",
url="https://cloud.google.com/vertex-ai/",
),
),
)
if original_exception.status_code == 401:
exception_mapping_worked = True
raise AuthenticationError(
message=f"VertexAIException - {original_exception.message}",
llm_provider=custom_llm_provider,
model=model,
)
if original_exception.status_code == 404:
exception_mapping_worked = True
raise NotFoundError(
message=f"VertexAIException - {original_exception.message}",
llm_provider=custom_llm_provider,
model=model,
)
if original_exception.status_code == 408:
exception_mapping_worked = True
raise Timeout(
message=f"VertexAIException - {original_exception.message}",
llm_provider=custom_llm_provider,
model=model,
)
if original_exception.status_code == 429:
exception_mapping_worked = True
raise RateLimitError(
message=f"litellm.RateLimitError: VertexAIException - {error_str}",
model=model,
llm_provider="vertex_ai",
litellm_debug_info=extra_information,
response=httpx.Response(
status_code=429,
request=httpx.Request(
method="POST",
url=" https://cloud.google.com/vertex-ai/",
),
),
)
if original_exception.status_code == 500:
exception_mapping_worked = True
raise litellm.InternalServerError(
message=f"VertexAIException InternalServerError - {error_str}",
model=model,
llm_provider="vertex_ai",
litellm_debug_info=extra_information,
response=httpx.Response(
status_code=500,
content=str(original_exception),
request=httpx.Request(method="completion", url="https://github.com/BerriAI/litellm"), # type: ignore
),
)
if original_exception.status_code == 503:
exception_mapping_worked = True
raise ServiceUnavailableError(
message=f"VertexAIException - {original_exception.message}",
llm_provider=custom_llm_provider,
model=model,
)
elif custom_llm_provider == "palm" or custom_llm_provider == "gemini":

@ishaan-jaff ishaan-jaff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 nit @timelfrink

Comment thread litellm/llms/vertex_ai/gemini/transformation.py Outdated

@ishaan-jaff ishaan-jaff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@ishaan-jaff
ishaan-jaff merged commit 9d7942e into BerriAI:main Sep 15, 2025
5 of 6 checks passed
data["cachedContent"] = cached_content
if labels is not None:
# Only add labels for Vertex AI endpoints (not Google GenAI/AI Studio) and only if non-empty
if labels and custom_llm_provider != LlmProviders.GEMINI:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timelfrink we should not do this check in here

it should be in the gemini/chat/transformation -

convert_generic_image_chunk_to_openai_image_obj,

this is a cleaner separation

ishaan-jaff pushed a commit that referenced this pull request Sep 18, 2025
* Add comprehensive tests for Vertex AI Gemini labels provider filtering

- Test Google GenAI endpoints exclude labels even when explicitly provided
- Test Vertex AI endpoints include labels when provided
- Cover provider detection logic for different endpoint URLs
- Verify metadata-to-labels conversion only happens for Vertex AI
- Ensure edge cases are handled properly (null/empty api_base)

* Fix Vertex AI Gemini labels field provider-aware filtering

- Add _is_google_genai_endpoint() function to detect Google GenAI vs Vertex AI endpoints
- Update _transform_request_body() to accept api_base parameter
- Only include labels field for Vertex AI endpoints (not Google GenAI)
- Pass api_base through sync/async transform functions
- Maintain backward compatibility with existing usage
- Fixes issue where Google GenAI requests failed with unsupported labels field

* Refactor labels filtering to use custom_llm_provider instead of URL parsing

Replace URL-based endpoint detection with custom_llm_provider parameter
checking for cleaner, more reliable provider identification.

Changes:
- Remove _is_google_genai_endpoint() helper function
- Update labels condition to use custom_llm_provider != "gemini"
- Remove api_base parameter from _transform_request_body()
- Simplify sync/async transform function signatures
- Update tests to reflect new parameter structure
- Remove obsolete test_provider_detection test

This approach aligns with existing codebase patterns where
custom_llm_provider="gemini" identifies Google AI Studio endpoints
that don't support labels, while vertex_ai/vertex_ai_beta identify
Vertex AI endpoints that do support labels.

* Use LlmProviders.GEMINI constant instead of hardcoded string
miguelangelmorenochacon added a commit to CartoDB/litellm that referenced this pull request Oct 28, 2025
- Add LlmProviders import for provider filtering
- Add metadata-to-labels conversion for OpenAI-style metadata
- Add provider-aware filtering to exclude labels from Google GenAI/AI Studio
- Update tests to verify correct behavior:
  * Labels are included for Vertex AI endpoints
  * Labels are excluded for Google GenAI endpoints
  * Metadata is converted to labels only for Vertex AI
- Remove extra_body handling (not in upstream implementation)

This aligns with the upstream BerriAI/litellm implementation that was
merged through PRs BerriAI#9175, BerriAI#14563, and BerriAI#14448.

Fixes BerriAI#13692

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
miguelangelmorenochacon pushed a commit to CartoDB/litellm that referenced this pull request Oct 28, 2025
…14563)

* Add comprehensive tests for Vertex AI Gemini labels provider filtering

- Test Google GenAI endpoints exclude labels even when explicitly provided
- Test Vertex AI endpoints include labels when provided
- Cover provider detection logic for different endpoint URLs
- Verify metadata-to-labels conversion only happens for Vertex AI
- Ensure edge cases are handled properly (null/empty api_base)

* Fix Vertex AI Gemini labels field provider-aware filtering

- Add _is_google_genai_endpoint() function to detect Google GenAI vs Vertex AI endpoints
- Update _transform_request_body() to accept api_base parameter
- Only include labels field for Vertex AI endpoints (not Google GenAI)
- Pass api_base through sync/async transform functions
- Maintain backward compatibility with existing usage
- Fixes issue where Google GenAI requests failed with unsupported labels field

* Refactor labels filtering to use custom_llm_provider instead of URL parsing

Replace URL-based endpoint detection with custom_llm_provider parameter
checking for cleaner, more reliable provider identification.

Changes:
- Remove _is_google_genai_endpoint() helper function
- Update labels condition to use custom_llm_provider != "gemini"
- Remove api_base parameter from _transform_request_body()
- Simplify sync/async transform function signatures
- Update tests to reflect new parameter structure
- Remove obsolete test_provider_detection test

This approach aligns with existing codebase patterns where
custom_llm_provider="gemini" identifies Google AI Studio endpoints
that don't support labels, while vertex_ai/vertex_ai_beta identify
Vertex AI endpoints that do support labels.

* Use LlmProviders.GEMINI constant instead of hardcoded string
mateo-di added a commit to CartoDB/litellm that referenced this pull request Nov 12, 2025
The upstream cherry-pick removed CARTO's provider-specific labels logic from PR BerriAI#14563.
Google AI Studio (gemini provider) should NOT include labels field.

This commit preserves BOTH fixes:
- Upstream metadata None check (from commit 944176f)
- CARTO provider-aware filtering (from PR BerriAI#14563 / commit 5748d6d)
mateo-di added a commit to CartoDB/litellm that referenced this pull request Nov 14, 2025
* Optional `labels` field in Vertex AI request

If the client sets the `labels` field in the request to the LiteLLM:
- pass the `labels` field to the Vertex AI backend

If the client sets the `metadata` field in the request to the LiteLLM:
- if the `labels` field is not set, fill it with `metadata` key/value
  pairs for all string values

* fix: check if metadata is not none

* fix: remove tests for function not yet in carto/main

The check_if_part_exists_in_parts function exists in upstream-sync-resolver/24
but not in carto/main yet. Removing tests until that branch is merged.

* fix: restore provider-aware labels filtering

The upstream cherry-pick removed CARTO's provider-specific labels logic from PR BerriAI#14563.
Google AI Studio (gemini provider) should NOT include labels field.

This commit preserves BOTH fixes:
- Upstream metadata None check (from commit 944176f)
- CARTO provider-aware filtering (from PR BerriAI#14563 / commit 5748d6d)

---------

Co-authored-by: Vedran Vidović <vvidovic@gmail.com>
Co-authored-by: Krrish Dholakia <krrishdholakia@gmail.com>
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
…14563)

* Add comprehensive tests for Vertex AI Gemini labels provider filtering

- Test Google GenAI endpoints exclude labels even when explicitly provided
- Test Vertex AI endpoints include labels when provided
- Cover provider detection logic for different endpoint URLs
- Verify metadata-to-labels conversion only happens for Vertex AI
- Ensure edge cases are handled properly (null/empty api_base)

* Fix Vertex AI Gemini labels field provider-aware filtering

- Add _is_google_genai_endpoint() function to detect Google GenAI vs Vertex AI endpoints
- Update _transform_request_body() to accept api_base parameter
- Only include labels field for Vertex AI endpoints (not Google GenAI)
- Pass api_base through sync/async transform functions
- Maintain backward compatibility with existing usage
- Fixes issue where Google GenAI requests failed with unsupported labels field

* Refactor labels filtering to use custom_llm_provider instead of URL parsing

Replace URL-based endpoint detection with custom_llm_provider parameter
checking for cleaner, more reliable provider identification.

Changes:
- Remove _is_google_genai_endpoint() helper function
- Update labels condition to use custom_llm_provider != "gemini"
- Remove api_base parameter from _transform_request_body()
- Simplify sync/async transform function signatures
- Update tests to reflect new parameter structure
- Remove obsolete test_provider_detection test

This approach aligns with existing codebase patterns where
custom_llm_provider="gemini" identifies Google AI Studio endpoints
that don't support labels, while vertex_ai/vertex_ai_beta identify
Vertex AI endpoints that do support labels.

* Use LlmProviders.GEMINI constant instead of hardcoded string
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: requests to Google GenAI fail because of an extra "labels" field

4 participants