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:
@@ -1,9 +1,11 @@
|
|||||||
import base64
|
import base64
|
||||||
|
import copy
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
from typing import AsyncIterator, List, Optional
|
from typing import AsyncIterator, List, Optional
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
import pydantic_core
|
||||||
from google.genai import Client, errors
|
from google.genai import Client, errors
|
||||||
from google.genai.types import (
|
from google.genai.types import (
|
||||||
FunctionCallingConfig,
|
FunctionCallingConfig,
|
||||||
@@ -78,6 +80,18 @@ class GoogleVertexClient(LLMClientBase):
|
|||||||
config=request_data["config"],
|
config=request_data["config"],
|
||||||
)
|
)
|
||||||
return response.model_dump()
|
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:
|
except Exception as e:
|
||||||
raise self.handle_llm_error(e)
|
raise self.handle_llm_error(e)
|
||||||
|
|
||||||
@@ -100,6 +114,19 @@ class GoogleVertexClient(LLMClientBase):
|
|||||||
contents=request_data["contents"],
|
contents=request_data["contents"],
|
||||||
config=request_data["config"],
|
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:
|
except errors.APIError as e:
|
||||||
# Retry on 503 and 500 errors as well, usually ephemeral from Gemini
|
# Retry on 503 and 500 errors as well, usually ephemeral from Gemini
|
||||||
if e.code == 503 or e.code == 500 or e.code == 504:
|
if e.code == 503 or e.code == 500 or e.code == 504:
|
||||||
@@ -156,6 +183,18 @@ class GoogleVertexClient(LLMClientBase):
|
|||||||
contents=request_data["contents"],
|
contents=request_data["contents"],
|
||||||
config=request_data["config"],
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error streaming {self._provider_name()} request: {e} with request data: {json.dumps(request_data)}")
|
logger.error(f"Error streaming {self._provider_name()} request: {e} with request data: {json.dumps(request_data)}")
|
||||||
raise e
|
raise e
|
||||||
@@ -196,7 +235,7 @@ class GoogleVertexClient(LLMClientBase):
|
|||||||
# Per https://ai.google.dev/gemini-api/docs/function-calling?example=meeting#notes_and_limitations
|
# Per https://ai.google.dev/gemini-api/docs/function-calling?example=meeting#notes_and_limitations
|
||||||
# * Only a subset of the OpenAPI schema is supported.
|
# * Only a subset of the OpenAPI schema is supported.
|
||||||
# * Supported parameter types in Python are limited.
|
# * 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]
|
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:
|
for key_to_remove in keys_to_remove_at_this_level:
|
||||||
logger.debug(f"Removing unsupported keyword '{key_to_remove}' from schema part.")
|
logger.debug(f"Removing unsupported keyword '{key_to_remove}' from schema part.")
|
||||||
@@ -273,7 +312,8 @@ class GoogleVertexClient(LLMClientBase):
|
|||||||
dict(
|
dict(
|
||||||
name=t.function.name,
|
name=t.function.name,
|
||||||
description=t.function.description,
|
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
|
for t in tools
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user