diff --git a/litellm/llms/prompt_templates/factory.py b/litellm/llms/prompt_templates/factory.py index f81515e98..7ec21e8bb 100644 --- a/litellm/llms/prompt_templates/factory.py +++ b/litellm/llms/prompt_templates/factory.py @@ -1010,6 +1010,9 @@ def convert_to_gemini_tool_call_invoke( name = tool["function"].get("name", "") arguments = tool["function"].get("arguments", "") arguments_dict = json.loads(arguments) + function_call: Optional[litellm.types.llms.vertex_ai.FunctionCall] = ( + None + ) for k, v in arguments_dict.items(): inferred_protocol_value = infer_protocol_value(value=v) _field = litellm.types.llms.vertex_ai.Field( @@ -1022,9 +1025,18 @@ def convert_to_gemini_tool_call_invoke( name=name, args=_fields, ) - _parts_list.append( - litellm.types.llms.vertex_ai.PartType(function_call=function_call) - ) + if function_call is not None: + _parts_list.append( + litellm.types.llms.vertex_ai.PartType( + function_call=function_call + ) + ) + else: # don't silently drop params. Make it clear to user what's happening. + raise Exception( + "function_call missing. Received tool call with 'type': 'function'. No function call in argument - {}".format( + tool + ) + ) return _parts_list except Exception as e: raise Exception( diff --git a/litellm/llms/vertex_httpx.py b/litellm/llms/vertex_httpx.py index b92730613..8af8f6894 100644 --- a/litellm/llms/vertex_httpx.py +++ b/litellm/llms/vertex_httpx.py @@ -491,6 +491,16 @@ class VertexGeminiConfig: "SPII": "The token generation was stopped as the response was flagged for Sensitive Personally Identifiable Information (SPII) contents.", } + def translate_exception_str(self, exception_string: str): + if ( + "GenerateContentRequest.tools[0].function_declarations[0].parameters.properties: should be non-empty for OBJECT type" + in exception_string + ): + return "'properties' field in tools[0]['function']['parameters'] cannot be empty if 'type' == 'object'. Received error from provider - {}".format( + exception_string + ) + return exception_string + async def make_call( client: Optional[AsyncHTTPHandler], @@ -504,8 +514,15 @@ async def make_call( if client is None: client = AsyncHTTPHandler() # Create a new client if none provided - response = await client.post(api_base, headers=headers, data=data, stream=True) - + try: + response = await client.post(api_base, headers=headers, data=data, stream=True) + response.raise_for_status() + except httpx.HTTPStatusError as e: + exception_string = str(await e.response.aread()) + raise VertexAIError( + status_code=e.response.status_code, + message=VertexGeminiConfig().translate_exception_str(exception_string), + ) if response.status_code != 200: raise VertexAIError(status_code=response.status_code, message=response.text) diff --git a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py index 511ce1ee9..966593551 100644 --- a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py @@ -384,8 +384,16 @@ async def pass_through_request( params=requested_query_params, headers=headers, ) + response = await async_client.send(req, stream=stream) + try: + response.raise_for_status() + except httpx.HTTPStatusError as e: + raise HTTPException( + status_code=e.response.status_code, detail=await e.response.aread() + ) + # Create an async generator to yield the response content async def stream_response() -> AsyncIterable[bytes]: async for chunk in response.aiter_bytes(): @@ -409,6 +417,13 @@ async def pass_through_request( response.headers.get("content-type") is not None and response.headers["content-type"] == "text/event-stream" ): + try: + response.raise_for_status() + except httpx.HTTPStatusError as e: + raise HTTPException( + status_code=e.response.status_code, detail=await e.response.aread() + ) + # streaming response # Create an async generator to yield the response content async def stream_response() -> AsyncIterable[bytes]: @@ -421,6 +436,13 @@ async def pass_through_request( status_code=response.status_code, ) + try: + response.raise_for_status() + except httpx.HTTPStatusError as e: + raise HTTPException( + status_code=e.response.status_code, detail=e.response.text + ) + if response.status_code >= 300: raise HTTPException(status_code=response.status_code, detail=response.text)