fix(core): handle Google GenAI validation errors (#9307)

* fix: handle const keyword in google genai tool schemas

* fix: handle pydantic ValidationError in Google GenAI client

Fixes Datadog error tracking issue where pydantic_core.ValidationError
was raised when tool schemas contained unsupported fields (e.g., 'const',
'default', 'additionalProperties').

Changes:
- Add error handling for pydantic ValidationError in request(), request_async(), and stream_async()
- Convert validation errors to LLMBadRequestError with helpful error message
- Deep copy tool parameters before cleaning to avoid modifying shared objects
- Add imports for pydantic_core and copy module

This prevents unhandled exceptions and provides better diagnostics when
tool schemas contain fields not supported by Google AI API.
This commit is contained in:
Kian Jones
2026-02-05 17:28:37 -08:00
committed by Caren Thomas
parent be60697a62
commit 3709be28dd

View File

@@ -1,9 +1,11 @@
import base64
import copy
import json
import uuid
from typing import AsyncIterator, List, Optional
import httpx
import pydantic_core
from google.genai import Client, errors
from google.genai.types import (
FunctionCallingConfig,
@@ -78,6 +80,18 @@ class GoogleVertexClient(LLMClientBase):
config=request_data["config"],
)
return response.model_dump()
except pydantic_core._pydantic_core.ValidationError as e:
# Handle Pydantic validation errors from the Google SDK
# This occurs when tool schemas contain unsupported fields
logger.error(
f"Pydantic validation error when calling {self._provider_name()} API. Tool schema contains unsupported fields. Error: {e}"
)
raise LLMBadRequestError(
message=f"Invalid tool schema for {self._provider_name()}: Tool parameters contain unsupported fields. "
f"Common issues: 'const', 'default', 'additionalProperties' are not supported by Google AI. "
f"Please check your tool definitions. Error: {str(e)}",
code=ErrorCode.INTERNAL_SERVER_ERROR,
)
except Exception as e:
raise self.handle_llm_error(e)
@@ -100,6 +114,19 @@ class GoogleVertexClient(LLMClientBase):
contents=request_data["contents"],
config=request_data["config"],
)
except pydantic_core._pydantic_core.ValidationError as e:
# Handle Pydantic validation errors from the Google SDK
# This occurs when tool schemas contain unsupported fields
logger.error(
f"Pydantic validation error when calling {self._provider_name()} API. "
f"Tool schema contains unsupported fields. Error: {e}"
)
raise LLMBadRequestError(
message=f"Invalid tool schema for {self._provider_name()}: Tool parameters contain unsupported fields. "
f"Common issues: 'const', 'default', 'additionalProperties' are not supported by Google AI. "
f"Please check your tool definitions. Error: {str(e)}",
code=ErrorCode.INTERNAL_SERVER_ERROR,
)
except errors.APIError as e:
# Retry on 503 and 500 errors as well, usually ephemeral from Gemini
if e.code == 503 or e.code == 500 or e.code == 504:
@@ -156,6 +183,18 @@ class GoogleVertexClient(LLMClientBase):
contents=request_data["contents"],
config=request_data["config"],
)
except pydantic_core._pydantic_core.ValidationError as e:
# Handle Pydantic validation errors from the Google SDK
# This occurs when tool schemas contain unsupported fields
logger.error(
f"Pydantic validation error when calling {self._provider_name()} API. Tool schema contains unsupported fields. Error: {e}"
)
raise LLMBadRequestError(
message=f"Invalid tool schema for {self._provider_name()}: Tool parameters contain unsupported fields. "
f"Common issues: 'const', 'default', 'additionalProperties' are not supported by Google AI. "
f"Please check your tool definitions. Error: {str(e)}",
code=ErrorCode.INTERNAL_SERVER_ERROR,
)
except Exception as e:
logger.error(f"Error streaming {self._provider_name()} request: {e} with request data: {json.dumps(request_data)}")
raise e
@@ -196,7 +235,7 @@ class GoogleVertexClient(LLMClientBase):
# Per https://ai.google.dev/gemini-api/docs/function-calling?example=meeting#notes_and_limitations
# * Only a subset of the OpenAPI schema is supported.
# * Supported parameter types in Python are limited.
unsupported_keys = ["default", "exclusiveMaximum", "exclusiveMinimum", "additionalProperties", "$schema"]
unsupported_keys = ["default", "exclusiveMaximum", "exclusiveMinimum", "additionalProperties", "$schema", "const"]
keys_to_remove_at_this_level = [key for key in unsupported_keys if key in schema_part]
for key_to_remove in keys_to_remove_at_this_level:
logger.debug(f"Removing unsupported keyword '{key_to_remove}' from schema part.")
@@ -273,7 +312,8 @@ class GoogleVertexClient(LLMClientBase):
dict(
name=t.function.name,
description=t.function.description,
parameters=t.function.parameters, # TODO need to unpack
# Deep copy parameters to avoid modifying the original Tool object
parameters=copy.deepcopy(t.function.parameters) if t.function.parameters else {},
)
for t in tools
]